diff --git a/cosmic-config/src/lib.rs b/cosmic-config/src/lib.rs index e51b4f6a..1396b5cb 100644 --- a/cosmic-config/src/lib.rs +++ b/cosmic-config/src/lib.rs @@ -48,12 +48,23 @@ fn get_state_dir() -> Option { return Some(PathBuf::from(home).join(".local").join("state")); } } - if let Some(state_dir) = dirs::state_dir() { - return Some(state_dir); + dirs::state_dir() +} + +/// Get the data directory, with Flatpak sandbox support. +fn get_data_dir() -> Option { + // Check if we're running in Flatpak + if env::var_os("FLATPAK_ID").is_some() { + // Try HOST_XDG_DATA_HOME first + if let Some(host_data) = env::var_os("HOST_XDG_DATA_HOME") { + return Some(PathBuf::from(host_data)); + } + // Fallback: try to construct from HOME + if let Some(home) = env::var_os("HOME") { + return Some(PathBuf::from(home).join(".local").join("share")); + } } - // On platforms without state_dir, fall back to ~/.local/state - let home_dir = dirs::home_dir()?; - Some(home_dir.join(".local").join(".state")) + dirs::data_dir() } #[cfg(feature = "subscription")] @@ -316,6 +327,40 @@ impl Config { }) } + /// Get data for the given application name and config version. + /// pub fn new_state(name: &str, version: u64) -> Result { + pub fn new_data(name: &str, version: u64) -> Result { + Self::new_data_inner(name, version, true) + } + + pub fn new_data_inner( + name: &str, + version: u64, + look_for_previous: bool, + ) -> Result { + // Look for [name]/v[version] + let path = sanitize_name(name)?.join(format!("v{}", version)); + + // Get libcosmic user data directory + let mut user_path = get_data_dir().ok_or(Error::NoConfigDirectory)?; + user_path.push("cosmic"); + user_path.push(path); + // Create new data directory if not found. + fs::create_dir_all(&user_path)?; + + Ok(Self { + system_path: None, + user_path: Some(user_path), + previous: if version > 1 && look_for_previous { + Self::new_data_inner(name, version - 1, false) + .ok() + .map(Box::new) + } else { + None + }, + }) + } + // Start a transaction (to set multiple configs at the same time) #[inline] pub fn transaction(&self) -> ConfigTransaction<'_> { diff --git a/examples/config/src/main.rs b/examples/config/src/main.rs index f6fb5c0d..dfcc5b99 100644 --- a/examples/config/src/main.rs +++ b/examples/config/src/main.rs @@ -88,4 +88,7 @@ pub fn main() { println!("Testing state"); test_config(Config::new_state("com.system76.Example", 1).unwrap()); + + println!("Testing data"); + test_config(Config::new_data("com.system76.Example", 1).unwrap()); } diff --git a/examples/context-menu/src/main.rs b/examples/context-menu/src/main.rs index 50c76bfb..e5ca5878 100644 --- a/examples/context-menu/src/main.rs +++ b/examples/context-menu/src/main.rs @@ -30,7 +30,6 @@ pub enum Message { WindowClose, Surface(cosmic::surface::Action), ToggleHideContent, - ToggleSomeAction, WindowNew, } @@ -83,7 +82,6 @@ impl cosmic::Application for App { /// Handle application events here. fn update(&mut self, message: Self::Message) -> Task { - tracing::error!("Clicked {message:?}"); match message { Message::Clicked => { self.button_label = format!("Clicked {message:?}"); @@ -93,10 +91,9 @@ impl cosmic::Application for App { cosmic::app::Action::Surface(action), )); } - Message::WindowClose - | Message::ToggleHideContent - | Message::ToggleSomeAction - | Message::WindowNew => {} + Message::WindowClose => {} + Message::ToggleHideContent => {} + Message::WindowNew => {} } Task::none() @@ -105,7 +102,7 @@ impl cosmic::Application for App { /// Creates a view after each update. fn view(&self) -> Element<'_, Self::Message> { let widget = cosmic::widget::context_menu( - cosmic::widget::button::text(self.button_label.clone()).on_press(Message::Clicked), + cosmic::widget::button::text(self.button_label.to_string()).on_press(Message::Clicked), self.context_menu(), ) .on_surface_action(Message::Surface); @@ -129,20 +126,12 @@ impl App { menu::Item::Divider, menu::Item::Folder( "View", - vec![ - menu::Item::CheckBox( - "Hide content", - None, - self.hide_content, - ContextMenuAction::ToggleHideContent, - ), - menu::Item::CheckBox( - "Test content", - None, - self.hide_content, - ContextMenuAction::ToggleSomeAction, - ), - ], + vec![menu::Item::CheckBox( + "Hide content", + None, + self.hide_content, + ContextMenuAction::ToggleHideContent, + )], ), menu::Item::Divider, menu::Item::Button("Quit", None, ContextMenuAction::WindowClose), @@ -155,7 +144,6 @@ impl App { pub enum ContextMenuAction { WindowClose, ToggleHideContent, - ToggleSomeAction, WindowNew, } @@ -165,7 +153,6 @@ impl menu::Action for ContextMenuAction { match self { ContextMenuAction::WindowClose => Message::WindowClose, ContextMenuAction::ToggleHideContent => Message::ToggleHideContent, - ContextMenuAction::ToggleSomeAction => Message::ToggleSomeAction, ContextMenuAction::WindowNew => Message::WindowNew, } } diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs index 35933b42..900d0322 100644 --- a/examples/menu/src/main.rs +++ b/examples/menu/src/main.rs @@ -9,7 +9,7 @@ use std::{env, process}; use cosmic::app::{Core, Settings, Task}; use cosmic::iced::alignment::{Horizontal, Vertical}; use cosmic::iced::keyboard::Key; -use cosmic::iced::{event, keyboard, window, Event, Length, Size, Subscription}; +use cosmic::iced::{window, Length, Size}; use cosmic::widget::menu::action::MenuAction; use cosmic::widget::menu::key_bind::{KeyBind, Modifier}; use cosmic::widget::menu::{self, ItemHeight, ItemWidth}; @@ -112,26 +112,6 @@ impl cosmic::Application for App { vec![menu_bar(&self.config, &self.key_binds)] } - fn subscription(&self) -> Subscription { - event::listen_with(|event, _status, _window_id| { - let Event::Keyboard(keyboard::Event::KeyPressed { - key, - modifiers, - physical_key, - .. - }) = event - else { - return None; - }; - - let key_binds = key_binds(); - key_binds - .iter() - .find(|(key_bind, _)| key_bind.matches(modifiers, &key, Some(&physical_key))) - .map(|(_, action)| action.message()) - }) - } - /// Handle application events here. fn update(&mut self, message: Self::Message) -> Task { match message { diff --git a/iced b/iced index e891f34b..c34af044 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit e891f34b3224b25fc4bdf286b3f9dba26c8abe5b +Subproject commit c34af044311c986e5f8458cf61bab2508aca48fa diff --git a/src/widget/menu/menu_bar.rs b/src/widget/menu/menu_bar.rs index 50dc9b64..72dff7d4 100644 --- a/src/widget/menu/menu_bar.rs +++ b/src/widget/menu/menu_bar.rs @@ -634,7 +634,6 @@ where return; } shell.capture_event(); - shell.request_redraw(); #[cfg(wayland_platform)] if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) { self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state); diff --git a/src/widget/menu/menu_inner.rs b/src/widget/menu/menu_inner.rs index 0bc3f6cf..2466d206 100644 --- a/src/widget/menu/menu_inner.rs +++ b/src/widget/menu/menu_inner.rs @@ -650,13 +650,12 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> { state.pressed = false; // process close condition - if state.open - && state - .view_cursor - .position() - .unwrap_or_default() - .distance(view_cursor.position().unwrap_or_default()) - < 2.0 + if state + .view_cursor + .position() + .unwrap_or_default() + .distance(view_cursor.position().unwrap_or_default()) + < 2.0 { let is_inside = state.menu_states[..=if self.is_overlay { state.active_root.len().saturating_sub(1) @@ -1429,48 +1428,47 @@ where state.view_cursor = view_cursor; // * remove invalid menus - if state.open { - let mut prev_bounds = std::iter::once(menu.bar_bounds) - .chain( - if menu.is_overlay { - state.menu_states[..state.menu_states.len().saturating_sub(1)].iter() - } else { - state.menu_states[..menu.depth].iter() - } - .map(|s| s.menu_bounds.children_bounds), - ) - .collect::>(); - if menu.is_overlay && menu.close_condition.leave { - for i in (0..state.menu_states.len()).rev() { - let mb = &state.menu_states[i].menu_bounds; - - if mb.parent_bounds.contains(overlay_cursor) - || menu.is_overlay && mb.children_bounds.contains(overlay_cursor) - || mb.offset_bounds.contains(overlay_cursor) - || (mb.check_bounds.contains(overlay_cursor) - && prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor))) - { - break; - } - prev_bounds.pop(); - state.active_root.pop(); - state.menu_states.pop(); + let mut prev_bounds = std::iter::once(menu.bar_bounds) + .chain( + if menu.is_overlay { + state.menu_states[..state.menu_states.len().saturating_sub(1)].iter() + } else { + state.menu_states[..menu.depth].iter() } - } else if menu.is_overlay { - for i in (0..state.menu_states.len()).rev() { - let mb = &state.menu_states[i].menu_bounds; + .map(|s| s.menu_bounds.children_bounds), + ) + .collect::>(); - if mb.parent_bounds.contains(overlay_cursor) - || mb.children_bounds.contains(overlay_cursor) - || prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor)) - { - break; - } - prev_bounds.pop(); - state.active_root.pop(); - state.menu_states.pop(); + if menu.is_overlay && menu.close_condition.leave { + for i in (0..state.menu_states.len()).rev() { + let mb = &state.menu_states[i].menu_bounds; + + if mb.parent_bounds.contains(overlay_cursor) + || menu.is_overlay && mb.children_bounds.contains(overlay_cursor) + || mb.offset_bounds.contains(overlay_cursor) + || (mb.check_bounds.contains(overlay_cursor) + && prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor))) + { + break; } + prev_bounds.pop(); + state.active_root.pop(); + state.menu_states.pop(); + } + } else if menu.is_overlay { + for i in (0..state.menu_states.len()).rev() { + let mb = &state.menu_states[i].menu_bounds; + + if mb.parent_bounds.contains(overlay_cursor) + || mb.children_bounds.contains(overlay_cursor) + || prev_bounds.iter().all(|pvb| !pvb.contains(overlay_cursor)) + { + break; + } + prev_bounds.pop(); + state.active_root.pop(); + state.menu_states.pop(); } } diff --git a/src/widget/popover.rs b/src/widget/popover.rs index b9704244..fb904248 100644 --- a/src/widget/popover.rs +++ b/src/widget/popover.rs @@ -24,7 +24,6 @@ pub enum Position { #[default] Center, Bottom, - Top, Point(Point), } @@ -260,7 +259,6 @@ where Position::Point(relative) => { bounds.position() + Vector::new(relative.x, relative.y) } - Position::Top => Point::new(bounds.x + bounds.width / 2.0, bounds.y), }; // Round position to prevent rendering issues @@ -355,12 +353,6 @@ where position.x = (position.x - width / 2.0).clamp(0.0, bounds.width - width); position.y = (position.y - height / 2.0).clamp(0.0, bounds.height - height); } - Position::Top => { - let width = node.size().width; - let height = node.size().height; - position.x = (position.x - width / 2.0).clamp(0.0, bounds.width - width); - position.y = (position.y - height).clamp(0.0, bounds.height - height); - } Position::Bottom => { // Position is set to the center bottom of the widget let width = node.size().width; diff --git a/src/widget/segmented_button/widget.rs b/src/widget/segmented_button/widget.rs index ed90328f..c5295562 100644 --- a/src/widget/segmented_button/widget.rs +++ b/src/widget/segmented_button/widget.rs @@ -1925,7 +1925,6 @@ where .. }) = event { - shell.request_redraw(); state.focused_visible = true; return if *modifiers == keyboard::Modifiers::SHIFT { self.focus_previous(state, shell);