wip: menus

This commit is contained in:
Ashley Wulber 2025-01-22 16:31:51 -05:00
parent a406859e65
commit 93e3d4fd42
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
25 changed files with 1358 additions and 243 deletions

View file

@ -3,17 +3,24 @@
use std::{cell::OnceCell, collections::HashMap};
use crate::widget::nav_bar;
use crate::{
theme::menu_bar,
widget::{
button, icon,
menu::{self, Tree},
nav_bar, responsive_container,
},
};
use cosmic_config::CosmicConfigEntry;
use cosmic_theme::ThemeMode;
use iced::window;
use iced_core::window::Id;
use iced::{window, Limits, Size};
use iced_core::{window::Id, Element};
use palette::Srgba;
use slotmap::Key;
use crate::Theme;
use super::{Element, Task};
use super::{message::SurfaceMessage, Renderer, Task};
/// Status of the nav bar and its panels.
#[derive(Clone)]
@ -97,6 +104,8 @@ pub struct Core {
pub(crate) main_window: Option<window::Id>,
pub(crate) exit_on_main_window_closed: bool,
pub(crate) menu_bars: HashMap<crate::widget::Id, (Limits, Size)>,
}
impl Default for Core {
@ -153,6 +162,7 @@ impl Default for Core {
portal_is_high_contrast: None,
main_window: None,
exit_on_main_window_closed: true,
menu_bars: HashMap::new(),
}
}
}
@ -376,4 +386,35 @@ impl Core {
std::mem::swap(&mut self.main_window, &mut id);
id
}
pub fn responsive_menu_bar<'a, Message: Clone + From<SurfaceMessage> + 'static>(
&self,
id: crate::widget::Id,
roots: Vec<Tree<'a, Message, Renderer>>,
) -> crate::Element<'a, Message> {
let menu_bar_size = self.menu_bars.get(&id);
if !menu_bar_size.is_some_and(|(limits, size)| {
let max_size = limits.max();
max_size.width < size.width
}) {
crate::Element::from(responsive_container::responsive_container(
menu::bar(roots),
id,
))
} else {
crate::Element::from(
responsive_container::responsive_container(
menu::bar(vec![menu::Tree::<'a, _, _>::with_children(
button::icon(icon::from_name("open-menu-symbolic"))
.padding([4, 12])
.class(crate::theme::Button::MenuRoot),
roots,
)]),
id,
)
.size(menu_bar_size.unwrap().1),
)
}
}
}

View file

@ -124,8 +124,7 @@ where
let message = match message {
super::Message::App(message) => {
match super::message::SurfaceMessageHandler::to_surface_message(message) {
super::message::MessageWrapper::Surface(surface_message) =>
{
super::message::MessageWrapper::Surface(surface_message) => {
#[cfg(feature = "wayland")]
match surface_message {
super::message::SurfaceMessage::Subsurface(settings, view) => {
@ -160,7 +159,6 @@ where
))
}
}
#[cfg(feature = "wayland")]
super::message::SurfaceMessage::Popup(settings, view) => {
let Some(settings) = std::sync::Arc::try_unwrap(settings)
.ok()
@ -200,7 +198,18 @@ where
super::message::SurfaceMessage::DestroySubsurface(id) => {
iced_winit::commands::subsurface::destroy_subsurface(id)
}
super::message::SurfaceMessage::ResponsiveMenuBar {
menu_bar,
limits,
size,
} => {
let core = self.app.core_mut();
core.menu_bars.insert(menu_bar, (limits, size));
iced::Task::none()
}
}
#[cfg(not(feature = "wayland"))]
iced::Task::none()
}
super::message::MessageWrapper::Message(message) => self.app.update(message),
}

View file

@ -15,6 +15,8 @@ pub(crate) mod multi_window;
pub mod settings;
pub mod message {
use iced::{Limits, Size};
pub enum MessageWrapper<M> {
Surface(SurfaceMessage),
Message(M),
@ -31,6 +33,7 @@ pub mod message {
}
}
/// Ignore this message in your application. It will be intercepted.
#[derive(Clone)]
pub enum SurfaceMessage {
/// Create a subsurface with a view function
@ -47,20 +50,39 @@ pub mod message {
),
/// Destroy a subsurface with a view function
DestroyPopup(iced::window::Id),
/// Responsive menu bar update
ResponsiveMenuBar {
/// Id of the menu bar
menu_bar: crate::widget::Id,
/// Limits of the menu bar
limits: Limits,
/// Requested Full Size for expanded menu bar
size: Size,
},
}
#[cfg(feature = "wayland")]
impl std::fmt::Debug for SurfaceMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SurfaceMessage::Subsurface(any, any1) => f.debug_tuple("Subsurface").finish(),
SurfaceMessage::DestroySubsurface(id) => {
f.debug_tuple("DestroySubsurface").field(id).finish()
Self::Subsurface(arg0, arg1) => {
f.debug_tuple("Subsurface").field(arg0).field(arg1).finish()
}
SurfaceMessage::Popup(any, any1) => f.debug_tuple("Popup").finish(),
SurfaceMessage::DestroyPopup(id) => {
f.debug_tuple("DestroyPopup").field(id).finish()
Self::DestroySubsurface(arg0) => {
f.debug_tuple("DestroySubsurface").field(arg0).finish()
}
Self::Popup(arg0, arg1) => f.debug_tuple("Popup").field(arg0).field(arg1).finish(),
Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(),
Self::ResponsiveMenuBar {
menu_bar,
limits,
size,
} => f
.debug_struct("ResponsiveMenuBar")
.field("menu_bar", menu_bar)
.field("limits", limits)
.field("size", size)
.finish(),
}
}
}
@ -149,6 +171,20 @@ pub mod message {
))
}
// TODO could this somehow be used by widgets? It is the wrong message type though.
// #[cfg(feature = "wayland")]
// pub fn get_simple_popup<App: super::Application>(
// settings: iced_runtime::platform_specific::wayland::popup::SctkPopupSettings,
// view: Option<
// impl Fn() -> crate::Element<'static, super::Message<App::Message>> + Send + Sync + 'static,
// >,
// ) -> App::Message
// where
// App::Message: SurfaceMessageHandler + From<SurfaceMessage>,
// {
// todo!()
// }
#[cfg(feature = "wayland")]
pub fn get_subsurface<App: super::Application>(
settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings + Send + Sync + 'static,
@ -477,7 +513,7 @@ impl DbusActivation {
pub fn run_single_instance<App: Application>(settings: Settings, flags: App::Flags) -> iced::Result
where
App::Flags: CosmicFlags,
App::Message: Clone + std::fmt::Debug + Send + 'static,
App::Message: Clone + std::fmt::Debug + Send + SurfaceMessageHandler + 'static,
{
let activation_token = std::env::var("XDG_ACTIVATION_TOKEN").ok();