Merge branch 'pop-os:master' into patch-1

This commit is contained in:
VandaLHJ 2025-09-19 07:12:43 +02:00 • committed by GitHub
commit a2acea10ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 152 additions and 31 deletions

11
i18n/ar/libcosmic.ftl Normal file
View file

@ -0,0 +1,11 @@
# Context Drawer
close = أغلق
# About
license = الترخيص
links = الروابط
developers = المطوّرون
designers = المصمّمون
artists = الفنانون
translators = المترجمون
documenters = الموثّقون

11
i18n/hu/libcosmic.ftl Normal file
View file

@ -0,0 +1,11 @@
# Context Drawer
close = Bezárás
# About
license = Licenc
links = Linkek
developers = Fejlesztők
designers = Tervezők
artists = Művészek
translators = Fordítók
documenters = Dokumentálók

11
i18n/pt-BR/libcosmic.ftl Normal file
View file

@ -0,0 +1,11 @@
# Context Drawer
close = Fechar
# About
license = Licença
links = Links
developers = Desenvolvedores
designers = Designers
artists = Artistas
translators = Tradutores
documenters = Documentadores

11
i18n/tr/libcosmic.ftl Normal file
View file

@ -0,0 +1,11 @@
# Context Drawer
close = Kapat
# About
license = Lisans
links = Bağlantılar
developers = Geliştiriciler
designers = Tasarımcılar
artists = Sanatçılar
translators = Çevirmenler
documenters = Belgelendiriciler

View file

@ -9,7 +9,12 @@ use super::{
}, },
menu_tree::MenuTree, menu_tree::MenuTree,
}; };
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(
feature = "multi-window",
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem}; use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem};
use crate::{ use crate::{
Renderer, Renderer,
@ -190,7 +195,7 @@ pub struct MenuBar<Message> {
menu_roots: Vec<MenuTree<Message>>, menu_roots: Vec<MenuTree<Message>>,
style: <crate::Theme as StyleSheet>::Style, style: <crate::Theme as StyleSheet>::Style,
window_id: window::Id, window_id: window::Id,
#[cfg(all(feature = "wayland", feature = "winit"))] #[cfg(all(feature = "multi-window", feature = "wayland", feature = "winit"))]
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner, positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
pub(crate) on_surface_action: pub(crate) on_surface_action:
Option<Arc<dyn Fn(crate::surface::Action) -> Message + Send + Sync + 'static>>, Option<Arc<dyn Fn(crate::surface::Action) -> Message + Send + Sync + 'static>>,
@ -225,7 +230,7 @@ where
menu_roots, menu_roots,
style: <crate::Theme as StyleSheet>::Style::default(), style: <crate::Theme as StyleSheet>::Style::default(),
window_id: window::Id::NONE, window_id: window::Id::NONE,
#[cfg(all(feature = "wayland", feature = "winit"))] #[cfg(all(feature = "multi-window", feature = "wayland", feature = "winit"))]
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner::default(), positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner::default(),
on_surface_action: None, on_surface_action: None,
} }
@ -319,7 +324,7 @@ where
self self
} }
#[cfg(all(feature = "wayland", feature = "winit"))] #[cfg(all(feature = "multi-window", feature = "wayland", feature = "winit"))]
pub fn with_positioner( pub fn with_positioner(
mut self, mut self,
positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner, positioner: iced_runtime::platform_specific::wayland::popup::SctkPositioner,
@ -351,7 +356,12 @@ where
self self
} }
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(
feature = "multi-window",
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn create_popup( fn create_popup(
&mut self, &mut self,
@ -630,7 +640,12 @@ where
if !create_popup { if !create_popup {
return event::Status::Ignored; return event::Status::Ignored;
} }
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(
feature = "multi-window",
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) { if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state); self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
} }
@ -638,7 +653,12 @@ where
Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered) Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered)
if open && view_cursor.is_over(layout.bounds()) => if open && view_cursor.is_over(layout.bounds()) =>
{ {
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(
feature = "multi-window",
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) { if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state); self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
} }
@ -715,7 +735,12 @@ where
_renderer: &Renderer, _renderer: &Renderer,
translation: Vector, translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(
feature = "multi-window",
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
&& self.on_surface_action.is_some() && self.on_surface_action.is_some()
&& self.window_id != window::Id::NONE && self.window_id != window::Id::NONE

View file

@ -4,7 +4,12 @@
use std::{borrow::Cow, sync::Arc}; use std::{borrow::Cow, sync::Arc};
use super::{menu_bar::MenuBarState, menu_tree::MenuTree}; use super::{menu_bar::MenuBarState, menu_tree::MenuTree};
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(
feature = "multi-window",
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem}; use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem};
use crate::style::menu_bar::StyleSheet; use crate::style::menu_bar::StyleSheet;
@ -663,6 +668,7 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
if needs_reset { if needs_reset {
#[cfg(all( #[cfg(all(
feature = "multi-window",
feature = "wayland", feature = "wayland",
feature = "winit", feature = "winit",
feature = "surface-message" feature = "surface-message"
@ -932,7 +938,12 @@ impl<Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, crate::
) -> event::Status { ) -> event::Status {
let (new_root, status) = self.on_event(event, layout, cursor, renderer, clipboard, shell); let (new_root, status) = self.on_event(event, layout, cursor, renderer, clipboard, shell);
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(
feature = "multi-window",
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) { if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
if let Some((new_root, new_ms)) = new_root { if let Some((new_root, new_ms)) = new_root {
use iced_runtime::platform_specific::wayland::popup::{ use iced_runtime::platform_specific::wayland::popup::{
@ -1177,7 +1188,12 @@ pub(crate) fn init_root_menu<Message: Clone>(
}); });
} }
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(
feature = "multi-window",
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
pub(super) fn init_root_popup_menu<Message>( pub(super) fn init_root_popup_menu<Message>(
menu: &mut Menu<'_, Message>, menu: &mut Menu<'_, Message>,
renderer: &crate::Renderer, renderer: &crate::Renderer,
@ -1474,7 +1490,7 @@ where
.as_ref() .as_ref()
.is_some_and(|i| *i != new_index && !active_menu[*i].children.is_empty()); .is_some_and(|i| *i != new_index && !active_menu[*i].children.is_empty());
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))] #[cfg(all(feature = "multi-window", feature = "wayland", feature = "winit", feature = "surface-message"))]
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) && remove { if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) && remove {
if let Some(id) = state.popup_id.remove(&menu.window_id) { if let Some(id) = state.popup_id.remove(&menu.window_id) {
state.active_root.truncate(menu.depth + 1); state.active_root.truncate(menu.depth + 1);

View file

@ -591,7 +591,10 @@ where
// Unfocus text input if it becomes disabled // Unfocus text input if it becomes disabled
if self.on_input.is_none() && !self.manage_value { if self.on_input.is_none() && !self.manage_value {
state.last_click = None; state.last_click = None;
state.is_focused = None; state.is_focused = state.is_focused.map(|mut f| {
f.focused = false;
f
});
state.is_pasting = None; state.is_pasting = None;
state.dragging_state = None; state.dragging_state = None;
} }
@ -628,18 +631,20 @@ where
state.dirty = true; state.dirty = true;
} }
if self.always_active && state.is_focused.is_none() { if self.always_active && !state.is_focused() {
let now = Instant::now(); let now = Instant::now();
LAST_FOCUS_UPDATE.with(|x| x.set(now)); LAST_FOCUS_UPDATE.with(|x| x.set(now));
state.is_focused = Some(Focus { state.is_focused = Some(Focus {
updated_at: now, updated_at: now,
now, now,
focused: true,
needs_update: false,
}); });
} }
// if the previous state was at the end of the text, keep it there // if the previous state was at the end of the text, keep it there
let old_value = Value::new(&old_value); let old_value = Value::new(&old_value);
if state.is_focused.is_some() { if state.is_focused() {
if let cursor::State::Index(index) = state.cursor.state(&old_value) { if let cursor::State::Index(index) = state.cursor.state(&old_value) {
if index == old_value.len() { if index == old_value.len() {
state.cursor.move_to(self.value.len()); state.cursor.move_to(self.value.len());
@ -647,7 +652,7 @@ where
}; };
} }
if let Some(f) = state.is_focused.as_ref() { if let Some(f) = state.is_focused.as_ref().filter(|f| f.focused) {
if f.updated_at != LAST_FOCUS_UPDATE.with(|f| f.get()) { if f.updated_at != LAST_FOCUS_UPDATE.with(|f| f.get()) {
state.unfocus(); state.unfocus();
state.emit_unfocus = true; state.emit_unfocus = true;
@ -834,13 +839,21 @@ where
let size = self.size.unwrap_or_else(|| renderer.default_size().0); let size = self.size.unwrap_or_else(|| renderer.default_size().0);
let line_height = self.line_height; let line_height = self.line_height;
// Disables editing of the editable variant when clicking outside of it. // Disables editing of the editable variant when clicking outside of, or for tab focus changes.
if self.is_editable_variant { if self.is_editable_variant {
if let Some(ref on_edit) = self.on_toggle_edit { if let Some(ref on_edit) = self.on_toggle_edit {
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
if !state.is_read_only && state.is_focused.is_none() { if !state.is_read_only && state.is_focused.is_some_and(|f| !f.focused) {
state.is_read_only = true; state.is_read_only = true;
shell.publish((on_edit)(false)); shell.publish((on_edit)(false));
} else if state.is_focused() && state.is_read_only {
state.is_read_only = false;
shell.publish((on_edit)(true));
} else if let Some(f) = state.is_focused.as_mut().filter(|f| f.needs_update) {
// TODO do we want to just move this to on_focus or on_unfocus for all inputs?
f.needs_update = false;
state.is_read_only = true;
shell.publish((on_edit)(f.focused));
} }
} }
} }
@ -1392,6 +1405,8 @@ pub fn update<'a, Message: Clone + 'static>(
state.is_focused = Some(Focus { state.is_focused = Some(Focus {
updated_at: now, updated_at: now,
now, now,
focused: true,
needs_update: false,
}); });
} }
@ -1520,7 +1535,7 @@ pub fn update<'a, Message: Clone + 'static>(
} }
// Focus on click of the text input, and ensure that the input is writable. // Focus on click of the text input, and ensure that the input is writable.
if state.is_focused.is_none() if !state.is_focused()
&& matches!(state.dragging_state, None | Some(DraggingState::Selection)) && matches!(state.dragging_state, None | Some(DraggingState::Selection))
{ {
if let Some(on_focus) = on_focus { if let Some(on_focus) = on_focus {
@ -1541,6 +1556,8 @@ pub fn update<'a, Message: Clone + 'static>(
state.is_focused = Some(Focus { state.is_focused = Some(Focus {
updated_at: now, updated_at: now,
now, now,
focused: true,
needs_update: false,
}); });
} }
@ -1592,8 +1609,7 @@ pub fn update<'a, Message: Clone + 'static>(
.. ..
}) => { }) => {
let state = state(); let state = state();
if let Some(focus) = state.is_focused.as_mut().filter(|f| f.focused) {
if let Some(focus) = &mut state.is_focused {
if state.is_read_only || (!manage_value && on_input.is_none()) { if state.is_read_only || (!manage_value && on_input.is_none()) {
return event::Status::Ignored; return event::Status::Ignored;
}; };
@ -1873,7 +1889,7 @@ pub fn update<'a, Message: Clone + 'static>(
Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => { Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => {
let state = state(); let state = state();
if state.is_focused.is_some() { if state.is_focused() {
match key { match key {
keyboard::Key::Character(c) if "v" == c => { keyboard::Key::Character(c) if "v" == c => {
state.is_pasting = None; state.is_pasting = None;
@ -1897,7 +1913,7 @@ pub fn update<'a, Message: Clone + 'static>(
Event::Window(window::Event::RedrawRequested(now)) => { Event::Window(window::Event::RedrawRequested(now)) => {
let state = state(); let state = state();
if let Some(focus) = &mut state.is_focused { if let Some(focus) = state.is_focused.as_mut().filter(|f| f.focused) {
focus.now = now; focus.now = now;
let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS let millis_until_redraw = CURSOR_BLINK_INTERVAL_MILLIS
@ -2258,12 +2274,16 @@ pub fn draw<'a, Message>(
let handling_dnd_offer = !matches!(state.dnd_offer, DndOfferState::None); let handling_dnd_offer = !matches!(state.dnd_offer, DndOfferState::None);
#[cfg(not(feature = "wayland"))] #[cfg(not(feature = "wayland"))]
let handling_dnd_offer = false; let handling_dnd_offer = false;
let (cursor, offset) = if let Some(focus) = &state.is_focused.or_else(|| { let (cursor, offset) = if let Some(focus) =
handling_dnd_offer.then(|| Focus { state.is_focused.filter(|f| f.focused).or_else(|| {
updated_at: Instant::now(), let now = Instant::now();
now: Instant::now(), handling_dnd_offer.then(|| Focus {
}) needs_update: false,
}) { updated_at: now,
now,
focused: true,
})
}) {
match state.cursor.state(value) { match state.cursor.state(value) {
cursor::State::Index(position) => { cursor::State::Index(position) => {
let (text_value_width, offset) = let (text_value_width, offset) =
@ -2547,6 +2567,8 @@ pub struct State {
struct Focus { struct Focus {
updated_at: Instant, updated_at: Instant,
now: Instant, now: Instant,
focused: bool,
needs_update: bool,
} }
impl State { impl State {
@ -2565,6 +2587,8 @@ impl State {
Focus { Focus {
updated_at: now, updated_at: now,
now, now,
focused: true,
needs_update: false,
} }
}), }),
select_on_focus, select_on_focus,
@ -2623,7 +2647,7 @@ impl State {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn is_focused(&self) -> bool { pub fn is_focused(&self) -> bool {
self.is_focused.is_some() self.is_focused.is_some_and(|f| f.focused)
} }
/// Returns the [`Cursor`] of the [`TextInput`]. /// Returns the [`Cursor`] of the [`TextInput`].
@ -2642,6 +2666,8 @@ impl State {
self.is_focused = Some(Focus { self.is_focused = Some(Focus {
updated_at: now, updated_at: now,
now, now,
focused: true,
needs_update: false,
}); });
if self.select_on_focus { if self.select_on_focus {
@ -2656,7 +2682,11 @@ impl State {
pub(super) fn unfocus(&mut self) { pub(super) fn unfocus(&mut self) {
self.move_cursor_to_front(); self.move_cursor_to_front();
self.last_click = None; self.last_click = None;
self.is_focused = None; self.is_focused = self.is_focused.map(|mut f| {
f.focused = false;
f.needs_update = false;
f
});
self.dragging_state = None; self.dragging_state = None;
self.is_pasting = None; self.is_pasting = None;
self.keyboard_modifiers = keyboard::Modifiers::default(); self.keyboard_modifiers = keyboard::Modifiers::default();
@ -2707,11 +2737,17 @@ impl operation::Focusable for State {
#[inline] #[inline]
fn focus(&mut self) { fn focus(&mut self) {
Self::focus(self); Self::focus(self);
if let Some(focus) = self.is_focused.as_mut() {
focus.needs_update = true;
}
} }
#[inline] #[inline]
fn unfocus(&mut self) { fn unfocus(&mut self) {
Self::unfocus(self); Self::unfocus(self);
if let Some(focus) = self.is_focused.as_mut() {
focus.needs_update = true;
}
} }
} }