Merge remote-tracking branch 'upstream/master'

# Conflicts:
#	iced
This commit is contained in:
Lionel DARNIS 2026-08-27 07:49:01 +02:00
commit 173d6ebd32
9 changed files with 105 additions and 108 deletions

View file

@ -48,23 +48,12 @@ fn get_state_dir() -> Option<PathBuf> {
return Some(PathBuf::from(home).join(".local").join("state"));
}
}
dirs::state_dir()
}
/// Get the data directory, with Flatpak sandbox support.
fn get_data_dir() -> Option<PathBuf> {
// 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"));
}
if let Some(state_dir) = dirs::state_dir() {
return Some(state_dir);
}
dirs::data_dir()
// On platforms without state_dir, fall back to ~/.local/state
let home_dir = dirs::home_dir()?;
Some(home_dir.join(".local").join(".state"))
}
#[cfg(feature = "subscription")]
@ -327,40 +316,6 @@ impl Config {
})
}
/// Get data for the given application name and config version.
/// pub fn new_state(name: &str, version: u64) -> Result<Self, Error> {
pub fn new_data(name: &str, version: u64) -> Result<Self, Error> {
Self::new_data_inner(name, version, true)
}
pub fn new_data_inner(
name: &str,
version: u64,
look_for_previous: bool,
) -> Result<Self, Error> {
// 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<'_> {

View file

@ -88,7 +88,4 @@ 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());
}

View file

@ -30,6 +30,7 @@ pub enum Message {
WindowClose,
Surface(cosmic::surface::Action),
ToggleHideContent,
ToggleSomeAction,
WindowNew,
}
@ -82,6 +83,7 @@ impl cosmic::Application for App {
/// Handle application events here.
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
tracing::error!("Clicked {message:?}");
match message {
Message::Clicked => {
self.button_label = format!("Clicked {message:?}");
@ -91,9 +93,10 @@ impl cosmic::Application for App {
cosmic::app::Action::Surface(action),
));
}
Message::WindowClose => {}
Message::ToggleHideContent => {}
Message::WindowNew => {}
Message::WindowClose
| Message::ToggleHideContent
| Message::ToggleSomeAction
| Message::WindowNew => {}
}
Task::none()
@ -102,7 +105,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.to_string()).on_press(Message::Clicked),
cosmic::widget::button::text(self.button_label.clone()).on_press(Message::Clicked),
self.context_menu(),
)
.on_surface_action(Message::Surface);
@ -126,12 +129,20 @@ impl App {
menu::Item::Divider,
menu::Item::Folder(
"View",
vec![menu::Item::CheckBox(
"Hide content",
None,
self.hide_content,
ContextMenuAction::ToggleHideContent,
)],
vec![
menu::Item::CheckBox(
"Hide content",
None,
self.hide_content,
ContextMenuAction::ToggleHideContent,
),
menu::Item::CheckBox(
"Test content",
None,
self.hide_content,
ContextMenuAction::ToggleSomeAction,
),
],
),
menu::Item::Divider,
menu::Item::Button("Quit", None, ContextMenuAction::WindowClose),
@ -144,6 +155,7 @@ impl App {
pub enum ContextMenuAction {
WindowClose,
ToggleHideContent,
ToggleSomeAction,
WindowNew,
}
@ -153,6 +165,7 @@ impl menu::Action for ContextMenuAction {
match self {
ContextMenuAction::WindowClose => Message::WindowClose,
ContextMenuAction::ToggleHideContent => Message::ToggleHideContent,
ContextMenuAction::ToggleSomeAction => Message::ToggleSomeAction,
ContextMenuAction::WindowNew => Message::WindowNew,
}
}

View file

@ -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::{window, Length, Size};
use cosmic::iced::{event, keyboard, window, Event, Length, Size, Subscription};
use cosmic::widget::menu::action::MenuAction;
use cosmic::widget::menu::key_bind::{KeyBind, Modifier};
use cosmic::widget::menu::{self, ItemHeight, ItemWidth};
@ -112,6 +112,26 @@ impl cosmic::Application for App {
vec![menu_bar(&self.config, &self.key_binds)]
}
fn subscription(&self) -> Subscription<Self::Message> {
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<Self::Message> {
match message {

2
iced

@ -1 +1 @@
Subproject commit c34af044311c986e5f8458cf61bab2508aca48fa
Subproject commit e891f34b3224b25fc4bdf286b3f9dba26c8abe5b

View file

@ -634,6 +634,7 @@ 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);

View file

@ -650,12 +650,13 @@ impl<'b, Message: Clone + 'static> Menu<'b, Message> {
state.pressed = false;
// process close condition
if state
.view_cursor
.position()
.unwrap_or_default()
.distance(view_cursor.position().unwrap_or_default())
< 2.0
if state.open
&& 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)
@ -1428,47 +1429,48 @@ 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::<Vec<_>>();
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()
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();
}
.map(|s| s.menu_bounds.children_bounds),
)
.collect::<Vec<_>>();
} else if menu.is_overlay {
for i in (0..state.menu_states.len()).rev() {
let mb = &state.menu_states[i].menu_bounds;
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;
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();
}
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();
}
}

View file

@ -24,6 +24,7 @@ pub enum Position {
#[default]
Center,
Bottom,
Top,
Point(Point),
}
@ -259,6 +260,7 @@ 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
@ -353,6 +355,12 @@ 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;

View file

@ -1925,6 +1925,7 @@ where
..
}) = event
{
shell.request_redraw();
state.focused_visible = true;
return if *modifiers == keyboard::Modifiers::SHIFT {
self.focus_previous(state, shell);