Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ libc = { version = "0.2.171", optional = true }
license = { version = "3.6.0", optional = true }
mime = { version = "0.3.17", optional = true }
palette = "0.7.6"
raw-window-handle = "0.6"
rfd = { version = "0.15.3", default-features = false, features = [
"xdg-portal",
], optional = true }
Expand Down
2 changes: 1 addition & 1 deletion iced
4 changes: 4 additions & 0 deletions src/app/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub enum Action {
NavBar(nav_bar::Id),
/// Activates a context menu for an item from the nav bar.
NavBarContext(nav_bar::Id),
/// A new window was opened.
Opened(iced::window::Id),
/// Set scaling factor
ScaleFactor(f32),
/// Show the window menu
Expand All @@ -60,6 +62,8 @@ pub enum Action {
ToolkitConfig(CosmicTk),
/// Window focus lost
Unfocus(iced::window::Id),
/// Windowing system initialized
WindowingSystemInitialized,
/// Updates the window maximized state
WindowMaximized(iced::window::Id, bool),
/// Updates the tracked window geometry.
Expand Down
75 changes: 74 additions & 1 deletion src/app/cosmic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,65 @@ use iced::{Task, window};
use iced_futures::event::listen_with;
use palette::color_difference::EuclideanDistance;

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub enum WindowingSystem {
UiKit,
AppKit,
Orbital,
OhosNdk,
Xlib,
Xcb,
Wayland,
Drm,
Gbm,
Win32,
WinRt,
Web,
WebCanvas,
WebOffscreenCanvas,
AndroidNdk,
Haiku,
}

pub(crate) static WINDOWING_SYSTEM: std::sync::OnceLock<WindowingSystem> =
std::sync::OnceLock::new();

pub fn windowing_system() -> Option<WindowingSystem> {
WINDOWING_SYSTEM.get().copied()
}

fn init_windowing_system<M>(handle: raw_window_handle::WindowHandle) -> crate::Action<M> {
let raw: &raw_window_handle::RawWindowHandle = handle.as_ref();
let system = match raw {
window::raw_window_handle::RawWindowHandle::UiKit(_) => WindowingSystem::UiKit,
window::raw_window_handle::RawWindowHandle::AppKit(_) => WindowingSystem::AppKit,
window::raw_window_handle::RawWindowHandle::Orbital(_) => WindowingSystem::Orbital,
window::raw_window_handle::RawWindowHandle::OhosNdk(_) => WindowingSystem::OhosNdk,
window::raw_window_handle::RawWindowHandle::Xlib(_) => WindowingSystem::Xlib,
window::raw_window_handle::RawWindowHandle::Xcb(_) => WindowingSystem::Xcb,
window::raw_window_handle::RawWindowHandle::Wayland(_) => WindowingSystem::Wayland,
window::raw_window_handle::RawWindowHandle::Web(_) => WindowingSystem::Web,
window::raw_window_handle::RawWindowHandle::WebCanvas(_) => WindowingSystem::WebCanvas,
window::raw_window_handle::RawWindowHandle::WebOffscreenCanvas(_) => {
WindowingSystem::WebOffscreenCanvas
}
window::raw_window_handle::RawWindowHandle::AndroidNdk(_) => WindowingSystem::AndroidNdk,
window::raw_window_handle::RawWindowHandle::Haiku(_) => WindowingSystem::Haiku,
window::raw_window_handle::RawWindowHandle::Drm(_) => WindowingSystem::Drm,
window::raw_window_handle::RawWindowHandle::Gbm(_) => WindowingSystem::Gbm,
window::raw_window_handle::RawWindowHandle::Win32(_) => WindowingSystem::Win32,
window::raw_window_handle::RawWindowHandle::WinRt(_) => WindowingSystem::WinRt,
_ => {
tracing::warn!("Unknown windowing system: {raw:?}");
return crate::Action::Cosmic(Action::WindowingSystemInitialized);
}
};

_ = WINDOWING_SYSTEM.set(system);
crate::Action::Cosmic(Action::WindowingSystemInitialized)
}

#[derive(Default)]
pub struct Cosmic<App: Application> {
pub app: App,
Expand All @@ -41,10 +100,17 @@ where
use iced_futures::futures::executor::block_on;
core.settings_daemon = block_on(cosmic_config::dbus::settings_daemon_proxy()).ok();
}
let id = core.main_window_id().unwrap_or(window::Id::RESERVED);

let (model, command) = T::init(core, flags);

(Self::new(model), command)
(
Self::new(model),
Task::batch(vec![
command,
iced_runtime::window::run_with_handle(id, init_windowing_system),
]),
)
}

#[cfg(not(feature = "multi-window"))]
Expand All @@ -57,6 +123,7 @@ where
self.app.title(id).to_string()
}

#[allow(clippy::too_many_lines)]
pub fn surface_update(
&mut self,
_surface_message: crate::surface::Action,
Expand Down Expand Up @@ -255,6 +322,9 @@ where
iced::Event::Window(window::Event::Resized(iced::Size { width, height })) => {
return Some(Action::WindowResize(id, width, height));
}
iced::Event::Window(window::Event::Opened { .. }) => {
return Some(Action::Opened(id));
}
iced::Event::Window(window::Event::Closed) => {
return Some(Action::SurfaceClosed(id));
}
Expand Down Expand Up @@ -786,6 +856,9 @@ impl<T: Application> Cosmic<T> {
let core = self.app.core_mut();
core.applet.suggested_bounds = b;
}
Action::Opened(id) => {
return iced_runtime::window::run_with_handle(id, init_windowing_system);
}
_ => {}
}

Expand Down
100 changes: 57 additions & 43 deletions src/widget/context_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

//! A context menu is a menu in a graphical user interface that appears upon user interaction, such as a right-click mouse operation.

#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem};
use crate::widget::menu::{
self, CloseCondition, Direction, ItemHeight, ItemWidth, MenuBarState, PathHighlight,
init_root_menu, menu_roots_diff,
Expand Down Expand Up @@ -361,21 +363,23 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
feature = "winit",
feature = "surface-message"
))]
state.menu_bar_state.inner.with_data_mut(|state| {
if let Some(id) = state.popup_id.remove(&self.window_id) {
state.menu_states.clear();
state.active_root.clear();
state.open = false;

{
let surface_action = self.on_surface_action.as_ref().unwrap();
shell.publish(surface_action(
crate::surface::action::destroy_popup(id),
));
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
state.menu_bar_state.inner.with_data_mut(|state| {
if let Some(id) = state.popup_id.remove(&self.window_id) {
state.menu_states.clear();
state.active_root.clear();
state.open = false;

{
let surface_action = self.on_surface_action.as_ref().unwrap();
shell.publish(surface_action(
crate::surface::action::destroy_popup(id),
));
}
state.view_cursor = cursor;
}
state.view_cursor = cursor;
}
});
});
}
}

_ => (),
Expand All @@ -392,41 +396,17 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
state.view_cursor = cursor;
});
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
self.create_popup(layout, cursor, renderer, shell, viewport, state);
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
self.create_popup(layout, cursor, renderer, shell, viewport, state);
}

return event::Status::Captured;
} else if right_button_released(&event)
|| (touch_lifted(&event))
|| left_button_released(&event)
{
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
state.menu_bar_state.inner.with_data_mut(|state| {
if let Some(id) = state.popup_id.remove(&self.window_id) {
state.menu_states.clear();
state.active_root.clear();
state.open = false;

{
let surface_action = self.on_surface_action.as_ref().unwrap();

shell
.publish(surface_action(crate::surface::action::destroy_popup(id)));
}
state.view_cursor = cursor;
}
});
}
} else if open {
match event {
Event::Mouse(mouse::Event::ButtonReleased(
mouse::Button::Right | mouse::Button::Left,
))
| Event::Touch(touch::Event::FingerLifted { .. }) => {
#[cfg(all(
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
state.menu_bar_state.inner.with_data_mut(|state| {
if let Some(id) = state.popup_id.remove(&self.window_id) {
state.menu_states.clear();
Expand All @@ -444,6 +424,37 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
}
});
}
}
} else if open {
match event {
Event::Mouse(mouse::Event::ButtonReleased(
mouse::Button::Right | mouse::Button::Left,
))
| Event::Touch(touch::Event::FingerLifted { .. }) => {
#[cfg(all(
feature = "wayland",
feature = "winit",
feature = "surface-message"
))]
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
state.menu_bar_state.inner.with_data_mut(|state| {
if let Some(id) = state.popup_id.remove(&self.window_id) {
state.menu_states.clear();
state.active_root.clear();
state.open = false;

{
let surface_action = self.on_surface_action.as_ref().unwrap();

shell.publish(surface_action(
crate::surface::action::destroy_popup(id),
));
}
state.view_cursor = cursor;
}
});
}
}
_ => (),
}
}
Expand All @@ -468,7 +479,10 @@ impl<Message: 'static + Clone> Widget<Message, crate::Theme, crate::Renderer>
translation: Vector,
) -> Option<iced_core::overlay::Element<'b, Message, crate::Theme, crate::Renderer>> {
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
if self.window_id != window::Id::NONE && self.on_surface_action.is_some() {
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
&& self.window_id != window::Id::NONE
&& self.on_surface_action.is_some()
{
return None;
}

Expand Down
17 changes: 14 additions & 3 deletions src/widget/menu/menu_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use super::{
},
menu_tree::MenuTree,
};
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
use crate::app::cosmic::{WINDOWING_SYSTEM, WindowingSystem};
use crate::{
Renderer,
style::menu_bar::StyleSheet,
Expand Down Expand Up @@ -629,13 +631,17 @@ where
return event::Status::Ignored;
}
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
}
}
Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::CursorEntered)
if open && view_cursor.is_over(layout.bounds()) =>
{
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland)) {
self.create_popup(layout, view_cursor, renderer, shell, viewport, my_state);
}
}
_ => (),
}
Expand Down Expand Up @@ -710,7 +716,12 @@ where
translation: Vector,
) -> Option<overlay::Element<'b, Message, crate::Theme, Renderer>> {
#[cfg(all(feature = "wayland", feature = "winit", feature = "surface-message"))]
return None;
if matches!(WINDOWING_SYSTEM.get(), Some(WindowingSystem::Wayland))
&& self.on_surface_action.is_some()
&& self.window_id != window::Id::NONE
{
return None;
}

let state = tree.state.downcast_ref::<MenuBarState>();
if state.inner.with_data(|state| !state.open) {
Expand Down
Loading
Loading