wip rebase updates

This commit is contained in:
Ashley Wulber 2026-02-10 15:37:41 -05:00
parent 86dcf8af6c
commit e10459fb37
68 changed files with 1776 additions and 1544 deletions

View file

@ -8,8 +8,6 @@ use crate::{config::CosmicTk, keyboard_nav};
#[cfg(feature = "wayland")]
use cctk::sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
use cosmic_theme::ThemeMode;
#[cfg(not(any(feature = "multi-window", feature = "wayland")))]
use iced::Application as IcedApplication;
/// A message managed internally by COSMIC.
#[derive(Clone, Debug)]

View file

@ -15,7 +15,7 @@ use cosmic_theme::ThemeMode;
use iced::Application as IcedApplication;
#[cfg(feature = "wayland")]
use iced::event::wayland;
use iced::{Task, window};
use iced::{Task, theme, window};
use iced_futures::event::listen_with;
#[cfg(feature = "wayland")]
use iced_winit::SurfaceIdWrapper;
@ -397,15 +397,16 @@ where
f64::from(self.app.core().scale_factor())
}
pub fn style(&self, theme: &Theme) -> iced_runtime::Appearance {
pub fn style(&self, theme: &Theme) -> theme::Style {
if let Some(style) = self.app.style() {
style
} else if self.app.core().window.is_maximized {
let theme = THEME.lock().unwrap();
crate::style::iced::application::appearance(theme.borrow())
crate::style::iced::application::style(theme.borrow())
} else {
let theme = THEME.lock().unwrap();
iced_runtime::Appearance {
theme::Style {
background_color: iced_core::Color::TRANSPARENT,
icon_color: theme.cosmic().on_bg_color().into(),
text_color: theme.cosmic().on_bg_color().into(),
@ -635,7 +636,7 @@ impl<T: Application> Cosmic<T> {
self.app.on_window_resize(id, width, height);
//TODO: more efficient test of maximized (winit has no event for maximize if set by the OS)
return iced::window::get_maximized(id).map(move |maximized| {
return iced::window::is_maximized(id).map(move |maximized| {
crate::Action::Cosmic(Action::WindowMaximized(id, maximized))
});
}
@ -711,10 +712,10 @@ impl<T: Application> Cosmic<T> {
Action::KeyboardNav(message) => match message {
keyboard_nav::Action::FocusNext => {
return iced::widget::focus_next().map(crate::Action::Cosmic);
return iced::widget::operation::focus_next().map(crate::Action::Cosmic);
}
keyboard_nav::Action::FocusPrevious => {
return iced::widget::focus_previous().map(crate::Action::Cosmic);
return iced::widget::operation::focus_previous().map(crate::Action::Cosmic);
}
keyboard_nav::Action::Escape => return self.app.on_escape(),
keyboard_nav::Action::Search => return self.app.on_search(),

View file

@ -11,9 +11,8 @@ pub use action::Action;
use cosmic_config::CosmicConfigEntry;
pub mod context_drawer;
pub use context_drawer::{ContextDrawer, context_drawer};
use iced::application::BootFn;
pub mod cosmic;
#[cfg(all(feature = "winit", feature = "multi-window"))]
pub(crate) mod multi_window;
pub mod settings;
pub type Task<M> = iced::Task<crate::Action<M>>;
@ -21,12 +20,13 @@ pub type Task<M> = iced::Task<crate::Action<M>>;
pub use crate::Core;
use crate::prelude::*;
use crate::theme::THEME;
use crate::widget::{container, horizontal_space, id_container, menu, nav_bar, popover};
use crate::widget::{container, id_container, menu, nav_bar, popover, space};
use apply::Apply;
use iced::window;
use iced::{Length, Subscription};
use iced::{theme, window};
pub use settings::Settings;
use std::borrow::Cow;
use std::{cell::RefCell, rc::Rc};
#[cold]
pub(crate) fn iced_settings<App: Application>(
@ -72,7 +72,7 @@ pub(crate) fn iced_settings<App: Application>(
core.exit_on_main_window_closed = exit_on_close;
if let Some(border_size) = settings.resizable {
window_settings.resize_border = border_size as u32;
// window_settings.resize_border = border_size as u32;
window_settings.resizable = true;
}
window_settings.decorations = !settings.client_decorations;
@ -82,7 +82,7 @@ pub(crate) fn iced_settings<App: Application>(
window_settings.min_size = Some(min_size);
}
let max_size = settings.size_limits.max();
if max_size != iced::Size::INFINITY {
if max_size != iced::Size::INFINITE {
window_settings.max_size = Some(max_size);
}
@ -90,6 +90,22 @@ pub(crate) fn iced_settings<App: Application>(
(iced, (core, flags), window_settings)
}
pub(crate) struct BootDataInner<A: crate::app::Application> {
pub flags: A::Flags,
pub core: Core,
}
pub(crate) struct BootData<A: crate::app::Application>(pub Rc<RefCell<Option<BootDataInner<A>>>>);
impl<A: crate::app::Application> BootFn<cosmic::Cosmic<A>, crate::Action<A::Message>>
for BootData<A>
{
fn boot(&self) -> (cosmic::Cosmic<A>, iced::Task<crate::Action<A::Message>>) {
let mut data = self.0.borrow_mut();
let data = data.take().unwrap();
cosmic::Cosmic::<A>::init((data.core, data.flags))
}
}
/// Launch a COSMIC application with the given [`Settings`].
///
/// # Errors
@ -102,39 +118,50 @@ pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Res
}
let default_font = settings.default_font;
let (settings, mut flags, window_settings) = iced_settings::<App>(settings, flags);
let (settings, (mut core, flags), window_settings) = iced_settings::<App>(settings, flags);
#[cfg(not(feature = "multi-window"))]
{
flags.0.main_window = Some(iced::window::Id::RESERVED);
core.main_window = Some(iced::window::Id::RESERVED);
iced::application(
cosmic::Cosmic::title,
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
flags,
core,
})))),
cosmic::Cosmic::update,
cosmic::Cosmic::view,
)
.subscription(cosmic::Cosmic::subscription)
.title(cosmic::Cosmic::title)
.style(cosmic::Cosmic::style)
.theme(cosmic::Cosmic::theme)
.window_size((500.0, 800.0))
.settings(settings)
.window(window_settings)
.run_with(move || cosmic::Cosmic::<App>::init(flags))
.run()
}
#[cfg(feature = "multi-window")]
{
let mut app = multi_window::multi_window::<_, _, _, _, App::Executor>(
cosmic::Cosmic::title,
let no_main_window = core.main_window.is_none();
if no_main_window {
// app = app.window(window_settings);
core.main_window = Some(iced_core::window::Id::RESERVED);
}
let mut app = iced::daemon(
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
flags,
core,
})))),
cosmic::Cosmic::update,
cosmic::Cosmic::view,
);
if flags.0.main_window.is_none() {
app = app.window(window_settings);
flags.0.main_window = Some(iced_core::window::Id::RESERVED);
}
app.subscription(cosmic::Cosmic::subscription)
.title(cosmic::Cosmic::title)
.style(cosmic::Cosmic::style)
.theme(cosmic::Cosmic::theme)
.settings(settings)
.run_with(move || cosmic::Cosmic::<App>::init(flags))
.run()
}
}
@ -204,13 +231,16 @@ where
tracing::info!("Another instance is running");
Ok(())
} else {
let (settings, mut flags, window_settings) = iced_settings::<App>(settings, flags);
flags.0.single_instance = true;
let (settings, (mut core, flags), window_settings) = iced_settings::<App>(settings, flags);
core.single_instance = true;
#[cfg(not(feature = "multi-window"))]
{
iced::application(
cosmic::Cosmic::title,
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
flags,
core,
})))),
cosmic::Cosmic::update,
cosmic::Cosmic::view,
)
@ -220,24 +250,30 @@ where
.window_size((500.0, 800.0))
.settings(settings)
.window(window_settings)
.run_with(move || cosmic::Cosmic::<App>::init(flags))
.run()
}
#[cfg(feature = "multi-window")]
{
let mut app = multi_window::multi_window::<_, _, _, _, App::Executor>(
cosmic::Cosmic::title,
let no_main_window = core.main_window.is_none();
if no_main_window {
// app = app.window(window_settings);
core.main_window = Some(iced_core::window::Id::RESERVED);
}
let mut app = iced::daemon(
BootData(Rc::new(RefCell::new(Some(BootDataInner::<App> {
flags,
core,
})))),
cosmic::Cosmic::update,
cosmic::Cosmic::view,
);
if flags.0.main_window.is_none() {
app = app.window(window_settings);
flags.0.main_window = Some(iced_core::window::Id::RESERVED);
}
app.subscription(cosmic::Cosmic::subscription)
.style(cosmic::Cosmic::style)
.title(cosmic::Cosmic::title)
.theme(cosmic::Cosmic::theme)
.settings(settings)
.run_with(move || cosmic::Cosmic::<App>::init(flags))
.run()
}
}
}
@ -428,7 +464,7 @@ where
}
/// Overrides the default style for applications
fn style(&self) -> Option<iced_runtime::Appearance> {
fn style(&self) -> Option<theme::Style> {
None
}
@ -667,7 +703,7 @@ impl<App: Application> ApplicationExt for App {
)
} else {
//TODO: this element is added to workaround state issues
widgets.push(horizontal_space().width(Length::Shrink).into());
widgets.push(space::horizontal().width(Length::Shrink).into());
}
}
}

View file

@ -1,244 +0,0 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
//! Create and run daemons that run in the background.
//! Copied from iced 0.13, but adds optional initial window
use iced::application;
use iced::window;
use iced::{
self, Program,
program::{self, with_style, with_subscription, with_theme, with_title},
runtime::{Appearance, DefaultStyle},
};
use iced::{Element, Result, Settings, Subscription, Task};
use std::marker::PhantomData;
pub(crate) struct Instance<State, Message, Theme, Renderer, Update, View, Executor> {
update: Update,
view: View,
_state: PhantomData<State>,
_message: PhantomData<Message>,
_theme: PhantomData<Theme>,
_renderer: PhantomData<Renderer>,
_executor: PhantomData<Executor>,
}
/// Creates an iced [`MultiWindow`] given its title, update, and view logic.
pub fn multi_window<State, Message, Theme, Renderer, Executor>(
title: impl Title<State>,
update: impl application::Update<State, Message>,
view: impl for<'a> self::View<'a, State, Message, Theme, Renderer>,
) -> MultiWindow<impl Program<State = State, Message = Message, Theme = Theme>>
where
State: 'static,
Message: Send + std::fmt::Debug + 'static,
Theme: Default + DefaultStyle,
Renderer: program::Renderer,
Executor: iced::Executor,
{
use std::marker::PhantomData;
impl<State, Message, Theme, Renderer, Update, View, Executor> Program
for Instance<State, Message, Theme, Renderer, Update, View, Executor>
where
Message: Send + std::fmt::Debug + 'static,
Theme: Default + DefaultStyle,
Renderer: program::Renderer,
Update: application::Update<State, Message>,
View: for<'a> self::View<'a, State, Message, Theme, Renderer>,
Executor: iced::Executor,
{
type State = State;
type Message = Message;
type Theme = Theme;
type Renderer = Renderer;
type Executor = Executor;
fn update(&self, state: &mut Self::State, message: Self::Message) -> Task<Self::Message> {
self.update.update(state, message).into()
}
fn view<'a>(
&self,
state: &'a Self::State,
window: window::Id,
) -> Element<'a, Self::Message, Self::Theme, Self::Renderer> {
self.view.view(state, window).into()
}
}
MultiWindow {
raw: Instance {
update,
view,
_state: PhantomData,
_message: PhantomData,
_theme: PhantomData,
_renderer: PhantomData,
_executor: PhantomData::<Executor>,
},
settings: Settings::default(),
window: None,
}
.title(title)
}
/// The underlying definition and configuration of an iced daemon.
///
/// You can use this API to create and run iced applications
/// step by step—without coupling your logic to a trait
/// or a specific type.
///
/// You can create a [`MultiWindow`] with the [`daemon`] helper.
#[derive(Debug)]
pub struct MultiWindow<P: Program> {
raw: P,
settings: Settings,
window: Option<window::Settings>,
}
impl<P: Program> MultiWindow<P> {
#[cfg(any(feature = "winit", feature = "wayland"))]
/// Runs the [`MultiWindow`].
///
/// The state of the [`MultiWindow`] must implement [`Default`].
/// If your state does not implement [`Default`], use [`run_with`]
/// instead.
///
/// [`run_with`]: Self::run_with
pub fn run(self) -> Result
where
Self: 'static,
P::State: Default,
{
self.raw.run(self.settings, self.window)
}
#[cfg(any(feature = "winit", feature = "wayland"))]
/// Runs the [`MultiWindow`] with a closure that creates the initial state.
pub fn run_with<I>(self, initialize: I) -> Result
where
Self: 'static,
I: FnOnce() -> (P::State, Task<P::Message>) + 'static,
{
self.raw.run_with(self.settings, self.window, initialize)
}
/// Sets the [`Settings`] that will be used to run the [`MultiWindow`].
pub fn settings(self, settings: Settings) -> Self {
Self { settings, ..self }
}
/// Sets the [`Title`] of the [`MultiWindow`].
pub(crate) fn title(
self,
title: impl Title<P::State>,
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
MultiWindow {
raw: with_title(self.raw, move |state, window| title.title(state, window)),
settings: self.settings,
window: self.window,
}
}
/// Sets the subscription logic of the [`MultiWindow`].
pub fn subscription(
self,
f: impl Fn(&P::State) -> Subscription<P::Message>,
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
MultiWindow {
raw: with_subscription(self.raw, f),
settings: self.settings,
window: self.window,
}
}
/// Sets the theme logic of the [`MultiWindow`].
pub fn theme(
self,
f: impl Fn(&P::State, window::Id) -> P::Theme,
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
MultiWindow {
raw: with_theme(self.raw, f),
settings: self.settings,
window: self.window,
}
}
/// Sets the style logic of the [`MultiWindow`].
pub fn style(
self,
f: impl Fn(&P::State, &P::Theme) -> Appearance,
) -> MultiWindow<impl Program<State = P::State, Message = P::Message, Theme = P::Theme>> {
MultiWindow {
raw: with_style(self.raw, f),
settings: self.settings,
window: self.window,
}
}
/// Sets the window settings of the [`MultiWindow`].
pub fn window(self, window: window::Settings) -> Self {
Self {
raw: self.raw,
settings: self.settings,
window: Some(window),
}
}
}
/// The title logic of some [`MultiWindow`].
///
/// This trait is implemented both for `&static str` and
/// any closure `Fn(&State, window::Id) -> String`.
///
/// This trait allows the [`daemon`] builder to take any of them.
pub trait Title<State> {
/// Produces the title of the [`MultiWindow`].
fn title(&self, state: &State, window: window::Id) -> String;
}
impl<State> Title<State> for &'static str {
fn title(&self, _state: &State, _window: window::Id) -> String {
(*self).to_string()
}
}
impl<T, State> Title<State> for T
where
T: Fn(&State, window::Id) -> String,
{
fn title(&self, state: &State, window: window::Id) -> String {
self(state, window)
}
}
/// The view logic of some [`MultiWindow`].
///
/// This trait allows the [`daemon`] builder to take any closure that
/// returns any `Into<Element<'_, Message>>`.
pub trait View<'a, State, Message, Theme, Renderer> {
/// Produces the widget of the [`MultiWindow`].
fn view(
&self,
state: &'a State,
window: window::Id,
) -> impl Into<Element<'a, Message, Theme, Renderer>>;
}
impl<'a, T, State, Message, Theme, Renderer, Widget> View<'a, State, Message, Theme, Renderer> for T
where
T: Fn(&'a State, window::Id) -> Widget,
State: 'static,
Widget: Into<Element<'a, Message, Theme, Renderer>>,
{
fn view(
&self,
state: &'a State,
window: window::Id,
) -> impl Into<Element<'a, Message, Theme, Renderer>> {
self(state, window)
}
}