Cosmic advanced text (#103)

* wip: update to use cosmic-advanced-text

* use cosmic-advanced-text branch of iced

* fix: line height and spacing for segmented button and update to get svg fix

* fix: spin button styling & spacing

* update iced to fix segmented button border radius

* feat: example improvements

* feat: helper for loading fonts

* feat: add focus style to button

* fix: slider height and iced fixed

* feat: hash icon width and height

* cleanup

* update ci

* refactor: always use lazy feature of iced

* update iced

* update iced

* cleanup & update iced

* update iced: new slider & tiny-skia quad updates

* update iced: fixes for tiny-skia quad rendering with edge case border radius

* re-export iced_runtime & iced_widget

* merge master

* udpate iced

* update iced

* update iced

* update iced

* fix: make rectangle_tracker subscription only return update if there is some

* feat: derive macro for loading a cosmic-config

* feat (cosmic-config): iced subscription

* fix (example): update to rectangle tracker subscription

* fix (cosmic-config)

* refactor(cosmic-config-derive): add support for types with generic parameters

* fix (cosmic-config): feature gate updates for subscription helpers

* feat: support for custom & system themes + move cosmic-theme to libcosmic

* feat: sorta hacky way of creating header bars for libcosmic + update iced to get support for resizable windows in iced-sctk

* update iced

* update and reexport sctk

* fix: applet border radius

* feat (cosmic-theme): add id and name methods

* fix(cosmic-theme): reexport palette from cosmic-theme

* fix(cosmic-config-derive): allow use with reexported cosmic-config

* feat: update iced with fix and refactor applet env vars

* update iced
This commit is contained in:
Ashley Wulber 2023-05-30 12:03:15 -04:00 committed by GitHub
parent a173794bed
commit e056e8c830
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
65 changed files with 3431 additions and 405 deletions

View file

@ -1,16 +1,16 @@
use cosmic_panel_config::{PanelAnchor, PanelSize};
use cosmic_panel_config::{CosmicPanelBackground, PanelAnchor, PanelSize};
use iced::{
alignment::{Horizontal, Vertical},
wayland::InitialSurface,
widget::{self, Container},
Color, Element, Length, Rectangle, Settings,
};
use iced_core::BorderRadius;
use iced_native::command::platform_specific::wayland::{
use iced_core::layout::Limits;
use iced_style::{button::StyleSheet, container::Appearance};
use iced_widget::runtime::command::platform_specific::wayland::{
popup::{SctkPopupSettings, SctkPositioner},
window::SctkWindowSettings,
};
use iced_style::{button::StyleSheet, container::Appearance};
use sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity};
use crate::{theme::Button, Renderer};
@ -19,14 +19,15 @@ pub use cosmic_panel_config;
const APPLET_PADDING: u32 = 8;
#[must_use]
pub fn applet_button_theme() -> Button {
Button::Custom {
active: Box::new(|t| iced_style::button::Appearance {
border_radius: BorderRadius::from(0.0),
border_radius: 0.0,
..t.active(&Button::Text)
}),
hover: Box::new(|t| iced_style::button::Appearance {
border_radius: BorderRadius::from(0.0),
border_radius: 0.0,
..t.hovered(&Button::Text)
}),
}
@ -36,6 +37,8 @@ pub fn applet_button_theme() -> Button {
pub struct CosmicAppletHelper {
pub size: Size,
pub anchor: PanelAnchor,
pub background: CosmicPanelBackground,
pub output_name: String,
}
#[derive(Clone, Debug)]
@ -51,18 +54,24 @@ impl Default for CosmicAppletHelper {
size: Size::PanelSize(
std::env::var("COSMIC_PANEL_SIZE")
.ok()
.and_then(|size| size.parse::<PanelSize>().ok())
.and_then(|size| ron::from_str(size.as_str()).ok())
.unwrap_or(PanelSize::S),
),
anchor: std::env::var("COSMIC_PANEL_ANCHOR")
.ok()
.and_then(|size| size.parse::<PanelAnchor>().ok())
.and_then(|size| ron::from_str(size.as_str()).ok())
.unwrap_or(PanelAnchor::Top),
background: std::env::var("COSMIC_PANEL_BACKGROUND")
.ok()
.and_then(|size| ron::from_str(size.as_str()).ok())
.unwrap_or(CosmicPanelBackground::ThemeDefault),
output_name: std::env::var("COSMIC_PANEL_OUTPUT").unwrap_or_default(),
}
}
}
impl CosmicAppletHelper {
#[must_use]
pub fn suggested_size(&self) -> (u16, u16) {
match &self.size {
Size::PanelSize(size) => match size {
@ -87,18 +96,20 @@ impl CosmicAppletHelper {
}
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn window_settings_with_flags<F>(&self, flags: F) -> Settings<F> {
let (width, height) = self.suggested_size();
let width = u32::from(width);
let height = u32::from(height);
Settings {
initial_surface: InitialSurface::XdgWindow(SctkWindowSettings {
iced_settings: iced_native::window::Settings {
size: (width + APPLET_PADDING * 2, height + APPLET_PADDING * 2),
min_size: Some((width + APPLET_PADDING * 2, height + APPLET_PADDING * 2)),
max_size: Some((width + APPLET_PADDING * 2, height + APPLET_PADDING * 2)),
..Default::default()
},
size: (width + APPLET_PADDING * 2, height + APPLET_PADDING * 2),
size_limits: Limits::NONE
.min_height(height as f32 + APPLET_PADDING as f32 * 2.0)
.max_height(height as f32 + APPLET_PADDING as f32 * 2.0)
.min_width(width as f32 + APPLET_PADDING as f32 * 2.0)
.max_width(width as f32 + APPLET_PADDING as f32 * 2.0),
resizable: None,
..Default::default()
}),
..crate::settings_with_flags(flags)
@ -124,7 +135,7 @@ impl CosmicAppletHelper {
&self,
content: impl Into<Element<'a, Message, Renderer>>,
) -> Container<'a, Message, Renderer> {
let (valign, halign) = match self.anchor {
let (vertical_align, horizontal_align) = match self.anchor {
PanelAnchor::Left => (Vertical::Center, Horizontal::Left),
PanelAnchor::Right => (Vertical::Center, Horizontal::Right),
PanelAnchor::Top => (Vertical::Top, Horizontal::Center),
@ -135,22 +146,23 @@ impl CosmicAppletHelper {
crate::theme::Container::custom(|theme| Appearance {
text_color: Some(theme.cosmic().background.on.into()),
background: Some(Color::from(theme.cosmic().background.base).into()),
border_radius: 12.0,
border_radius: 12.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
}),
))
.width(Length::Shrink)
.height(Length::Shrink)
.align_x(halign)
.align_y(valign)
.align_x(horizontal_align)
.align_y(vertical_align)
}
#[must_use]
#[allow(clippy::cast_possible_wrap)]
pub fn get_popup_settings(
&self,
parent: iced_native::window::Id,
id: iced_native::window::Id,
parent: iced_core::window::Id,
id: iced_core::window::Id,
size: Option<(u32, u32)>,
width_padding: Option<i32>,
height_padding: Option<i32>,

View file

@ -7,7 +7,7 @@ use std::future::Future;
pub struct Executor(tokio::runtime::Runtime);
#[cfg(feature = "tokio")]
impl iced_native::Executor for Executor {
impl iced::Executor for Executor {
fn new() -> Result<Self, iced::futures::io::Error> {
Ok(Self(
tokio::runtime::Builder::new_multi_thread()

View file

@ -7,7 +7,7 @@ use std::future::Future;
pub struct Executor(tokio::runtime::Runtime);
#[cfg(feature = "tokio")]
impl iced_native::Executor for Executor {
impl iced::Executor for Executor {
fn new() -> Result<Self, iced::futures::io::Error> {
// Current thread executor requires calling `block_on` to actually run
// futures. Main thread is busy with things other than running futures,

View file

@ -2,18 +2,24 @@
// SPDX-License-Identifier: MPL-2.0
pub use iced::Font;
pub const FONT: Font = Font::External {
name: "Fira Sans Regular",
bytes: include_bytes!("../res/Fira/FiraSans-Regular.otf"),
use iced::{
font::{load, Error},
Command,
};
pub const FONT_LIGHT: Font = Font::External {
name: "Fira Sans Light",
bytes: include_bytes!("../res/Fira/FiraSans-Light.otf"),
};
pub const FONT: Font = Font::with_name("Fira Sans Regular");
pub const FONT_DATA: &[u8] = include_bytes!("../res/Fira/FiraSans-Regular.otf");
pub const FONT_SEMIBOLD: Font = Font::External {
name: "Fira Sans SemiBold",
bytes: include_bytes!("../res/Fira/FiraSans-SemiBold.otf"),
};
pub const FONT_LIGHT: Font = Font::with_name("Fira Sans Light");
pub const FONT_LIGHT_DATA: &[u8] = include_bytes!("../res/Fira/FiraSans-Light.otf");
pub const FONT_SEMIBOLD: Font = Font::with_name("Fira Sans SemiBold");
pub const FONT_SEMIBOLD_DATA: &[u8] = include_bytes!("../res/Fira/FiraSans-SemiBold.otf");
pub fn load_fonts() -> Command<Result<(), Error>> {
Command::batch(vec![
load(FONT_DATA),
load(FONT_LIGHT_DATA),
load(FONT_SEMIBOLD_DATA),
])
}

View file

@ -3,7 +3,7 @@ use iced::{
keyboard::{self, KeyCode},
mouse, subscription, Command, Event, Subscription,
};
use iced_native::widget::{operation, Id, Operation};
use iced_core::widget::{operation, Id, Operation};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Message {
@ -14,7 +14,6 @@ pub enum Message {
Search,
}
#[must_use]
pub fn subscription() -> Subscription<Message> {
subscription::events_with(|event, status| match (event, status) {
// Focus
@ -61,7 +60,6 @@ pub fn subscription() -> Subscription<Message> {
}
/// Unfocuses any actively-focused widget.
#[must_use]
pub fn unfocus<Message: 'static>() -> Command<Message> {
Command::<Message>::widget(unfocus_operation())
}

View file

@ -3,16 +3,18 @@
#![allow(clippy::module_name_repetitions)]
pub use cosmic_config;
pub use cosmic_theme;
pub use iced;
pub use iced_lazy;
pub use iced_native;
pub use iced_runtime;
#[cfg(feature = "wayland")]
pub use iced_sctk;
pub use iced_style;
pub use iced_widget;
#[cfg(feature = "winit")]
pub use iced_winit;
#[cfg(feature = "wayland")]
pub use sctk;
#[cfg(feature = "applet")]
pub mod applet;
pub mod executor;

View file

@ -27,11 +27,8 @@ pub fn settings<Flags: Default>() -> iced::Settings<Flags> {
#[must_use]
pub fn settings_with_flags<Flags>(flags: Flags) -> iced::Settings<Flags> {
iced::Settings {
default_font: match font::FONT {
iced::Font::Default => None,
iced::Font::External { bytes, .. } => Some(bytes),
},
default_text_size: 18,
default_font: font::FONT,
default_text_size: 18.0,
..iced::Settings::with_flags(flags)
}
}

View file

@ -7,12 +7,13 @@ mod segmented_button;
use std::hash::Hash;
use std::hash::Hasher;
use std::rc::Rc;
use std::sync::Arc;
pub use self::segmented_button::SegmentedButton;
use cosmic_theme::Component;
use cosmic_theme::LayeredTheme;
use iced_core::BorderRadius;
use iced_core::renderer::BorderRadius;
use iced_style::application;
use iced_style::button;
use iced_style::checkbox;
@ -25,18 +26,18 @@ use iced_style::radio;
use iced_style::rule;
use iced_style::scrollable;
use iced_style::slider;
use iced_style::slider::Rail;
use iced_style::svg;
use iced_style::text;
use iced_style::text_input;
use iced_style::toggler;
use iced_core::{Background, Color};
use palette::Srgba;
type CosmicColor = ::palette::rgb::Srgba;
type CosmicComponent = cosmic_theme::Component<CosmicColor>;
type CosmicTheme = cosmic_theme::Theme<CosmicColor>;
type CosmicThemeCss = cosmic_theme::Theme<cosmic_theme::util::CssColor>;
pub type CosmicColor = ::palette::rgb::Srgba;
pub type CosmicComponent = cosmic_theme::Component<CosmicColor>;
pub type CosmicTheme = cosmic_theme::Theme<CosmicColor>;
pub type CosmicThemeCss = cosmic_theme::Theme<cosmic_theme::util::CssColor>;
lazy_static::lazy_static! {
pub static ref COSMIC_DARK: CosmicTheme = CosmicThemeCss::dark_default().into_srgba();
@ -57,16 +58,17 @@ lazy_static::lazy_static! {
};
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub enum ThemeType {
#[default]
Dark,
Light,
HighContrastDark,
HighContrastLight,
Custom(Arc<CosmicTheme>),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct Theme {
pub theme_type: ThemeType,
pub layer: cosmic_theme::Layer,
@ -80,6 +82,7 @@ impl Theme {
ThemeType::Light => &COSMIC_LIGHT,
ThemeType::HighContrastDark => &COSMIC_HC_DARK,
ThemeType::HighContrastLight => &COSMIC_HC_LIGHT,
ThemeType::Custom(ref t) => t.as_ref(),
}
}
@ -115,6 +118,14 @@ impl Theme {
}
}
#[must_use]
pub fn custom(theme: Arc<CosmicTheme>) -> Self {
Self {
theme_type: ThemeType::Custom(theme),
..Default::default()
}
}
/// get current container
/// can be used in a component that is intended to be a child of a `CosmicContainer`
#[must_use]
@ -218,8 +229,8 @@ impl button::StyleSheet for Theme {
let component = style.cosmic(self);
button::Appearance {
border_radius: match style {
Button::Link => BorderRadius::from(0.0),
_ => BorderRadius::from(24.0),
Button::Link => 0.0,
_ => 24.0,
},
background: match style {
Button::Link | Button::Text => None,
@ -301,7 +312,7 @@ impl checkbox::StyleSheet for Theme {
} else {
palette.background.base.into()
}),
checkmark_color: palette.accent.on.into(),
icon_color: palette.accent.on.into(),
border_radius: 4.0,
border_width: if is_checked { 0.0 } else { 1.0 },
border_color: if is_checked {
@ -318,7 +329,7 @@ impl checkbox::StyleSheet for Theme {
} else {
palette.background.base.into()
}),
checkmark_color: palette.background.on.into(),
icon_color: palette.background.on.into(),
border_radius: 4.0,
border_width: if is_checked { 0.0 } else { 1.0 },
border_color: neutral_7.into(),
@ -330,7 +341,7 @@ impl checkbox::StyleSheet for Theme {
} else {
palette.background.base.into()
}),
checkmark_color: palette.success.on.into(),
icon_color: palette.success.on.into(),
border_radius: 4.0,
border_width: if is_checked { 0.0 } else { 1.0 },
border_color: if is_checked {
@ -347,7 +358,7 @@ impl checkbox::StyleSheet for Theme {
} else {
palette.background.base.into()
}),
checkmark_color: palette.destructive.on.into(),
icon_color: palette.destructive.on.into(),
border_radius: 4.0,
border_width: if is_checked { 0.0 } else { 1.0 },
border_color: if is_checked {
@ -374,7 +385,7 @@ impl checkbox::StyleSheet for Theme {
} else {
neutral_10.into()
}),
checkmark_color: palette.accent.on.into(),
icon_color: palette.accent.on.into(),
border_radius: 4.0,
border_width: if is_checked { 0.0 } else { 1.0 },
border_color: if is_checked {
@ -391,7 +402,7 @@ impl checkbox::StyleSheet for Theme {
} else {
neutral_10.into()
}),
checkmark_color: self.current_container().on.into(),
icon_color: self.current_container().on.into(),
border_radius: 4.0,
border_width: if is_checked { 0.0 } else { 1.0 },
border_color: if is_checked {
@ -408,7 +419,7 @@ impl checkbox::StyleSheet for Theme {
} else {
neutral_10.into()
}),
checkmark_color: palette.success.on.into(),
icon_color: palette.success.on.into(),
border_radius: 4.0,
border_width: if is_checked { 0.0 } else { 1.0 },
border_color: if is_checked {
@ -425,7 +436,7 @@ impl checkbox::StyleSheet for Theme {
} else {
neutral_10.into()
}),
checkmark_color: palette.destructive.on.into(),
icon_color: palette.destructive.on.into(),
border_radius: 4.0,
border_width: if is_checked { 0.0 } else { 1.0 },
border_color: if is_checked {
@ -474,6 +485,7 @@ pub enum Container {
Secondary,
#[default]
Transparent,
HeaderBar,
Custom(Box<dyn Fn(&Theme) -> container::Appearance>),
}
@ -496,7 +508,18 @@ impl container::StyleSheet for Theme {
container::Appearance {
text_color: Some(Color::from(palette.background.on)),
background: Some(iced::Background::Color(palette.background.base.into())),
border_radius: 2.0,
border_radius: 2.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
}
}
Container::HeaderBar => {
let palette = self.cosmic();
container::Appearance {
text_color: Some(Color::from(palette.background.on)),
background: Some(iced::Background::Color(palette.background.base.into())),
border_radius: BorderRadius::from([16.0, 16.0, 0.0, 0.0]),
border_width: 0.0,
border_color: Color::TRANSPARENT,
}
@ -507,7 +530,7 @@ impl container::StyleSheet for Theme {
container::Appearance {
text_color: Some(Color::from(palette.primary.on)),
background: Some(iced::Background::Color(palette.primary.base.into())),
border_radius: 2.0,
border_radius: 2.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
}
@ -518,7 +541,7 @@ impl container::StyleSheet for Theme {
container::Appearance {
text_color: Some(Color::from(palette.secondary.on)),
background: Some(iced::Background::Color(palette.secondary.base.into())),
border_radius: 2.0,
border_radius: 2.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
}
@ -538,11 +561,15 @@ impl slider::StyleSheet for Theme {
//TODO: no way to set rail thickness
slider::Appearance {
rail_colors: (
cosmic.accent.base.into(),
//TODO: no way to set color before/after slider
Color::TRANSPARENT,
),
rail: Rail {
colors: (
cosmic.accent.base.into(),
//TODO: no way to set color before/after slider
Color::TRANSPARENT,
),
width: 4.0,
},
handle: slider::Handle {
shape: slider::HandleShape::Circle { radius: 10.0 },
color: cosmic.accent.base.into(),
@ -610,7 +637,8 @@ impl pick_list::StyleSheet for Theme {
border_radius: 24.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
icon_size: 0.7,
// icon_size: 0.7, // TODO: how to replace
handle_color: cosmic.on_bg_color().into(),
}
}
@ -856,7 +884,11 @@ impl scrollable::StyleSheet for Theme {
}
}
fn hovered(&self, _style: &Self::Style) -> scrollable::Scrollbar {
fn hovered(
&self,
_style: &Self::Style,
_is_mouse_over_scrollbar: bool,
) -> scrollable::Scrollbar {
let theme = self.cosmic();
scrollable::Scrollbar {
@ -948,7 +980,7 @@ pub enum Text {
Default,
Color(Color),
// TODO: Can't use dyn Fn since this must be copy
Custom(fn(&Theme) -> text::Appearance),
Custom(fn(&Theme) -> iced_widget::text::Appearance),
}
impl From<Color> for Text {
@ -957,16 +989,16 @@ impl From<Color> for Text {
}
}
impl text::StyleSheet for Theme {
impl iced_widget::text::StyleSheet for Theme {
type Style = Text;
fn appearance(&self, style: Self::Style) -> text::Appearance {
fn appearance(&self, style: Self::Style) -> iced_widget::text::Appearance {
match style {
Text::Accent => text::Appearance {
Text::Accent => iced_widget::text::Appearance {
color: Some(self.cosmic().accent.base.into()),
},
Text::Default => text::Appearance { color: None },
Text::Color(c) => text::Appearance { color: Some(c) },
Text::Default => iced_widget::text::Appearance { color: None },
Text::Color(c) => iced_widget::text::Appearance { color: Some(c) },
Text::Custom(f) => f(self),
}
}
@ -995,12 +1027,14 @@ impl text_input::StyleSheet for Theme {
border_radius: 8.0,
border_width: 1.0,
border_color: self.current_container().component.divider.into(),
icon_color: self.current_container().on.into(),
},
TextInput::Search => text_input::Appearance {
background: Color::from(bg).into(),
border_radius: 24.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
icon_color: self.current_container().on.into(),
},
}
}
@ -1016,12 +1050,14 @@ impl text_input::StyleSheet for Theme {
border_radius: 8.0,
border_width: 1.0,
border_color: palette.accent.base.into(),
icon_color: self.current_container().on.into(),
},
TextInput::Search => text_input::Appearance {
background: Color::from(bg).into(),
border_radius: 24.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
icon_color: self.current_container().on.into(),
},
}
}
@ -1037,12 +1073,14 @@ impl text_input::StyleSheet for Theme {
border_radius: 8.0,
border_width: 1.0,
border_color: palette.accent.base.into(),
icon_color: self.current_container().on.into(),
},
TextInput::Search => text_input::Appearance {
background: Color::from(bg).into(),
border_radius: 24.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
icon_color: self.current_container().on.into(),
},
}
}
@ -1065,4 +1103,12 @@ impl text_input::StyleSheet for Theme {
palette.accent.base.into()
}
fn disabled_color(&self, _style: &Self::Style) -> Color {
todo!()
}
fn disabled(&self, _style: &Self::Style) -> text_input::Appearance {
todo!()
}
}

View file

@ -3,7 +3,7 @@
use crate::widget::segmented_button::{Appearance, ItemAppearance, StyleSheet};
use crate::{theme::Theme, widget::segmented_button::ItemStatusAppearance};
use iced_core::{Background, BorderRadius};
use iced_core::{renderer::BorderRadius, Background};
use palette::{rgb::Rgb, Alpha};
#[derive(Default)]
@ -141,7 +141,7 @@ impl StyleSheet for Theme {
mod horizontal {
use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance};
use iced_core::{Background, BorderRadius};
use iced_core::{renderer::BorderRadius, Background};
use palette::{rgb::Rgb, Alpha};
pub fn selection_active(cosmic: &cosmic_theme::Theme<Alpha<Rgb, f32>>) -> ItemStatusAppearance {
@ -222,7 +222,7 @@ pub fn hover(
mod vertical {
use crate::widget::segmented_button::{ItemAppearance, ItemStatusAppearance};
use iced_core::{Background, BorderRadius};
use iced_core::{renderer::BorderRadius, Background};
use palette::{rgb::Rgb, Alpha};
pub fn selection_active(cosmic: &cosmic_theme::Theme<Alpha<Rgb, f32>>) -> ItemStatusAppearance {

View file

@ -1,13 +1,13 @@
use iced::widget::Container;
use iced::Size;
use iced_native::alignment;
use iced_native::event::{self, Event};
use iced_native::layout;
use iced_native::mouse;
use iced_native::overlay;
use iced_native::renderer;
use iced_native::widget::{Operation, Tree};
use iced_native::{Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Widget};
use iced_core::alignment;
use iced_core::event::{self, Event};
use iced_core::layout;
use iced_core::mouse;
use iced_core::overlay;
use iced_core::renderer;
use iced_core::widget::Tree;
use iced_core::{Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Widget};
pub use iced_style::container::{Appearance, StyleSheet};
@ -27,7 +27,7 @@ where
#[allow(missing_debug_implementations)]
pub struct AspectRatio<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet,
{
ratio: f32,
@ -36,7 +36,7 @@ where
impl<'a, Message, Renderer> AspectRatio<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet,
{
fn constrain_limits(&self, size: Size) -> Size {
@ -55,7 +55,7 @@ where
impl<'a, Message, Renderer> AspectRatio<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet,
{
/// Creates an empty [`Container`].
@ -92,14 +92,14 @@ where
/// Sets the maximum width of the [`Container`].
#[must_use]
pub fn max_width(mut self, max_width: u32) -> Self {
pub fn max_width(mut self, max_width: f32) -> Self {
self.container = self.container.max_width(max_width);
self
}
/// Sets the maximum height of the [`Container`] in pixels.
#[must_use]
pub fn max_height(mut self, max_height: u32) -> Self {
pub fn max_height(mut self, max_height: f32) -> Self {
self.container = self.container.max_height(max_height);
self
}
@ -142,14 +142,14 @@ where
impl<'a, Message, Renderer> Widget<Message, Renderer> for AspectRatio<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet,
{
fn children(&self) -> Vec<Tree> {
self.container.children()
}
fn diff(&self, tree: &mut Tree) {
fn diff(&mut self, tree: &mut Tree) {
self.container.diff(tree);
}
@ -169,8 +169,16 @@ where
self.container.layout(renderer, &custom_limits)
}
fn operate(&self, tree: &mut Tree, layout: Layout<'_>, operation: &mut dyn Operation<Message>) {
self.container.operate(tree, layout, operation);
fn operate(
&self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<
iced_core::widget::OperationOutputWrapper<Message>,
>,
) {
self.container.operate(tree, layout, renderer, operation);
}
fn on_event(
@ -228,7 +236,7 @@ where
}
fn overlay<'b>(
&'b self,
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -241,7 +249,7 @@ impl<'a, Message, Renderer> From<AspectRatio<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Message: 'a,
Renderer: 'a + iced_native::Renderer,
Renderer: 'a + iced_core::Renderer,
Renderer::Theme: StyleSheet,
{
fn from(column: AspectRatio<'a, Message, Renderer>) -> Element<'a, Message, Renderer> {

View file

@ -1,13 +1,13 @@
use cosmic_theme::LayeredTheme;
use iced::widget::Container;
use iced_native::alignment;
use iced_native::event::{self, Event};
use iced_native::layout;
use iced_native::mouse;
use iced_native::overlay;
use iced_native::renderer;
use iced_native::widget::{Operation, Tree};
use iced_native::{Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Widget};
use iced_core::alignment;
use iced_core::event::{self, Event};
use iced_core::layout;
use iced_core::mouse;
use iced_core::overlay;
use iced_core::renderer;
use iced_core::widget::Tree;
use iced_core::{Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Widget};
pub use iced_style::container::{Appearance, StyleSheet};
pub fn container<'a, Message: 'static, T>(
@ -25,7 +25,7 @@ where
#[allow(missing_debug_implementations)]
pub struct LayerContainer<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet + Clone + cosmic_theme::LayeredTheme,
{
layer: Option<cosmic_theme::Layer>,
@ -34,7 +34,7 @@ where
impl<'a, Message, Renderer> LayerContainer<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet + Clone + cosmic_theme::LayeredTheme,
<Renderer::Theme as StyleSheet>::Style: std::convert::From<crate::theme::Container>,
{
@ -83,14 +83,14 @@ where
/// Sets the maximum width of the [`LayerContainer`].
#[must_use]
pub fn max_width(mut self, max_width: u32) -> Self {
pub fn max_width(mut self, max_width: f32) -> Self {
self.container = self.container.max_width(max_width);
self
}
/// Sets the maximum height of the [`LayerContainer`] in pixels.
#[must_use]
pub fn max_height(mut self, max_height: u32) -> Self {
pub fn max_height(mut self, max_height: f32) -> Self {
self.container = self.container.max_height(max_height);
self
}
@ -133,14 +133,14 @@ where
impl<'a, Message, Renderer> Widget<Message, Renderer> for LayerContainer<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet + Clone + cosmic_theme::LayeredTheme,
{
fn children(&self) -> Vec<Tree> {
self.container.children()
}
fn diff(&self, tree: &mut Tree) {
fn diff(&mut self, tree: &mut Tree) {
self.container.diff(tree);
}
@ -156,8 +156,16 @@ where
self.container.layout(renderer, limits)
}
fn operate(&self, tree: &mut Tree, layout: Layout<'_>, operation: &mut dyn Operation<Message>) {
self.container.operate(tree, layout, operation);
fn operate(
&self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<
iced_core::widget::OperationOutputWrapper<Message>,
>,
) {
self.container.operate(tree, layout, renderer, operation);
}
fn on_event(
@ -222,7 +230,7 @@ where
}
fn overlay<'b>(
&'b self,
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -235,7 +243,7 @@ impl<'a, Message, Renderer> From<LayerContainer<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Message: 'a,
Renderer: 'a + iced_native::Renderer,
Renderer: 'a + iced_core::Renderer,
Renderer::Theme: StyleSheet + Clone + cosmic_theme::LayeredTheme,
{
fn from(column: LayerContainer<'a, Message, Renderer>) -> Element<'a, Message, Renderer> {

View file

@ -5,6 +5,7 @@ use crate::{theme, Element};
use apply::Apply;
use derive_setters::Setters;
use iced::{self, widget, Length};
use iced_core::renderer::BorderRadius;
use std::borrow::Cow;
#[must_use]
@ -74,12 +75,13 @@ impl<'a, Message: Clone + 'static> HeaderBar<'a, Message> {
});
let mut widget = widget::row(packed)
.height(Length::Units(50))
.height(Length::Fixed(50.0))
.padding(8)
.spacing(8)
.apply(widget::container)
.style(crate::theme::Container::HeaderBar)
.center_y()
.apply(widget::mouse_listener);
.apply(widget::mouse_area);
if let Some(message) = self.on_drag.clone() {
widget = widget.on_press(message);

View file

@ -71,7 +71,7 @@ impl<'a> IconSource<'a> {
let handle = if let Some(path) = icon {
svg::Handle::from_path(path)
} else {
eprintln!("svg icon '{:?}' size {} not found", self, size);
eprintln!("svg icon '{self:?}' size {size} not found");
svg::Handle::from_memory(Vec::new())
};
@ -79,7 +79,7 @@ impl<'a> IconSource<'a> {
} else if let Some(icon) = icon {
Handle::Image(icon.into())
} else {
eprintln!("icon '{:?}' size {} not found", self, size);
eprintln!("icon '{self:?}' size {size} not found");
Handle::Image(image::Handle::from_memory(Vec::new()))
}
}
@ -90,7 +90,13 @@ impl<'a> IconSource<'a> {
}
/// Get a handle to a raster image from memory.
pub fn raster_from_memory(bytes: impl Into<Cow<'static, [u8]>>) -> Self {
pub fn raster_from_memory(
bytes: impl Into<Cow<'static, [u8]>>
+ std::convert::AsRef<[u8]>
+ std::marker::Send
+ std::marker::Sync
+ 'static,
) -> Self {
IconSource::Handle(Handle::Image(image::Handle::from_memory(bytes)))
}
@ -98,7 +104,11 @@ impl<'a> IconSource<'a> {
pub fn raster_from_pixels(
width: u32,
height: u32,
pixels: impl Into<Cow<'static, [u8]>>,
pixels: impl Into<Cow<'static, [u8]>>
+ std::convert::AsRef<[u8]>
+ std::marker::Send
+ std::marker::Sync
+ 'static,
) -> Self {
IconSource::Handle(Handle::Image(image::Handle::from_pixels(
width, height, pixels,
@ -165,7 +175,7 @@ impl From<svg::Handle> for IconSource<'static> {
}
/// A lazily-generated icon.
#[derive(Hash, Setters)]
#[derive(Setters)]
pub struct Icon<'a> {
#[setters(skip)]
source: IconSource<'a>,
@ -181,6 +191,33 @@ pub struct Icon<'a> {
force_svg: bool,
}
// XXX Hopefully this will be enough precision
impl Hash for Icon<'_> {
#[allow(clippy::cast_possible_truncation)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.source.hash(state);
self.theme.hash(state);
self.style.hash(state);
self.size.hash(state);
self.content_fit.hash(state);
self.force_svg.hash(state);
match self.width {
Some(Length::Fill) => 0.hash(state),
Some(Length::Shrink) => 1.hash(state),
Some(Length::Fixed(v)) => ((v * 1000.0) as i32).hash(state),
Some(Length::FillPortion(p)) => p.hash(state),
None => 2.hash(state),
}
match self.height {
Some(Length::Fill) => 0.hash(state),
Some(Length::Shrink) => 1.hash(state),
Some(Length::Fixed(v)) => ((v * 1000.0) as i32).hash(state),
Some(Length::FillPortion(p)) => p.hash(state),
None => 2.hash(state),
}
}
}
/// A lazily-generated icon.
#[must_use]
pub fn icon<'a>(source: impl Into<IconSource<'a>>, size: u16) -> Icon<'a> {
@ -199,8 +236,8 @@ pub fn icon<'a>(source: impl Into<IconSource<'a>>, size: u16) -> Icon<'a> {
impl<'a> Icon<'a> {
fn raster_element<Message: 'static>(&self, handle: image::Handle) -> Element<'static, Message> {
Image::new(handle)
.width(self.width.unwrap_or(Length::Units(self.size)))
.height(self.height.unwrap_or(Length::Units(self.size)))
.width(self.width.unwrap_or(Length::Fixed(f32::from(self.size))))
.height(self.height.unwrap_or(Length::Fixed(f32::from(self.size))))
.content_fit(self.content_fit)
.into()
}
@ -208,8 +245,8 @@ impl<'a> Icon<'a> {
fn svg_element<Message: 'static>(&self, handle: svg::Handle) -> Element<'static, Message> {
svg::Svg::<Renderer>::new(handle)
.style(self.style.clone())
.width(self.width.unwrap_or(Length::Units(self.size)))
.height(self.height.unwrap_or(Length::Units(self.size)))
.width(self.width.unwrap_or(Length::Fixed(f32::from(self.size))))
.height(self.height.unwrap_or(Length::Fixed(f32::from(self.size))))
.content_fit(self.content_fit)
.into()
}
@ -228,7 +265,7 @@ impl<'a> Icon<'a> {
let mut source = IconSource::Name(Cow::Borrowed(""));
std::mem::swap(&mut source, &mut self.source);
iced_lazy::lazy(hash, move || -> Element<Message> {
iced::widget::lazy(hash, move |_| -> Element<Message> {
match source.load(self.size, self.theme.as_deref(), self.force_svg) {
Handle::Svg(handle) => self.svg_element(handle),
Handle::Image(handle) => self.raster_element(handle),

View file

@ -63,7 +63,7 @@ pub fn style(theme: &crate::Theme) -> iced::widget::container::Appearance {
iced::widget::container::Appearance {
text_color: Some(container.on.into()),
background: Some(Background::Color(container.base.into())),
border_radius: 8.0,
border_radius: 8.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
}

View file

@ -45,7 +45,7 @@ pub fn nav_bar_style(theme: &Theme) -> iced_style::container::Appearance {
iced_style::container::Appearance {
text_color: Some(cosmic.on_bg_color().into()),
background: Some(Background::Color(cosmic.primary.base.into())),
border_radius: 8.0,
border_radius: 8.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
}

View file

@ -3,13 +3,13 @@
//! A widget showing a popup in an overlay positioned relative to another widget.
use iced_native::event::{self, Event};
use iced_native::layout;
use iced_native::mouse;
use iced_native::overlay;
use iced_native::renderer;
use iced_native::widget::{Operation, Tree};
use iced_native::{Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Widget};
use iced_core::event::{self, Event};
use iced_core::layout;
use iced_core::mouse;
use iced_core::overlay;
use iced_core::renderer;
use iced_core::widget::{Operation, OperationOutputWrapper, Tree};
use iced_core::{Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Widget};
use std::cell::RefCell;
pub use iced_style::container::{Appearance, StyleSheet};
@ -43,15 +43,15 @@ impl<'a, Message, Renderer> Popover<'a, Message, Renderer> {
impl<'a, Message, Renderer> Widget<Message, Renderer> for Popover<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet,
{
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.content), Tree::new(&*self.popup.borrow())]
}
fn diff(&self, tree: &mut Tree) {
tree.diff_children(&[&self.content, &self.popup.borrow()])
fn diff(&mut self, tree: &mut Tree) {
tree.diff_children(&mut [&mut self.content, &mut self.popup.borrow_mut()]);
}
fn width(&self) -> Length {
@ -66,10 +66,16 @@ where
self.content.as_widget().layout(renderer, limits)
}
fn operate(&self, tree: &mut Tree, layout: Layout<'_>, operation: &mut dyn Operation<Message>) {
fn operate(
&self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation<OperationOutputWrapper<Message>>,
) {
self.content
.as_widget()
.operate(&mut tree.children[0], layout, operation)
.operate(&mut tree.children[0], layout, renderer, operation);
}
fn on_event(
@ -128,11 +134,11 @@ where
layout,
cursor_position,
viewport,
)
);
}
fn overlay<'b>(
&'b self,
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
_renderer: &Renderer,
@ -155,7 +161,7 @@ where
impl<'a, Message, Renderer> From<Popover<'a, Message, Renderer>> for Element<'a, Message, Renderer>
where
Message: 'static,
Renderer: iced_native::Renderer + 'static,
Renderer: iced_core::Renderer + 'static,
Renderer::Theme: StyleSheet,
{
fn from(popover: Popover<'a, Message, Renderer>) -> Self {
@ -171,7 +177,7 @@ struct Overlay<'a, 'b, Message, Renderer> {
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
for Overlay<'a, 'b, Message, Renderer>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
{
fn layout(&self, renderer: &Renderer, bounds: Size, mut position: Point) -> layout::Node {
// Position is set to the center bottom of the lower widget
@ -186,11 +192,16 @@ where
node
}
fn operate(&mut self, layout: Layout<'_>, operation: &mut dyn Operation<Message>) {
fn operate(
&mut self,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation<OperationOutputWrapper<Message>>,
) {
self.content
.borrow()
.as_widget()
.operate(self.tree, layout, operation)
.operate(self.tree, layout, renderer, operation);
}
fn on_event(
@ -246,6 +257,6 @@ where
layout,
cursor_position,
&bounds,
)
);
}
}

View file

@ -4,14 +4,14 @@ use iced::futures::channel::mpsc::UnboundedSender;
use iced::widget::Container;
pub use subscription::*;
use iced_native::alignment;
use iced_native::event::{self, Event};
use iced_native::layout;
use iced_native::mouse;
use iced_native::overlay;
use iced_native::renderer;
use iced_native::widget::{Operation, Tree};
use iced_native::{Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Widget};
use iced_core::alignment;
use iced_core::event::{self, Event};
use iced_core::layout;
use iced_core::mouse;
use iced_core::overlay;
use iced_core::renderer;
use iced_core::widget::Tree;
use iced_core::{Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Widget};
use std::{fmt::Debug, hash::Hash};
pub use iced_style::container::{Appearance, StyleSheet};
@ -44,7 +44,7 @@ where
#[allow(missing_debug_implementations)]
pub struct RectangleTrackingContainer<'a, Message, Renderer, I>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet,
{
tx: UnboundedSender<(I, Rectangle)>,
@ -54,7 +54,7 @@ where
impl<'a, Message, Renderer, I> RectangleTrackingContainer<'a, Message, Renderer, I>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet,
I: 'a + Hash + Copy + Send + Sync + Debug,
{
@ -93,14 +93,14 @@ where
/// Sets the maximum width of the [`Container`].
#[must_use]
pub fn max_width(mut self, max_width: u32) -> Self {
pub fn max_width(mut self, max_width: f32) -> Self {
self.container = self.container.max_width(max_width);
self
}
/// Sets the maximum height of the [`Container`] in pixels.
#[must_use]
pub fn max_height(mut self, max_height: u32) -> Self {
pub fn max_height(mut self, max_height: f32) -> Self {
self.container = self.container.max_height(max_height);
self
}
@ -144,7 +144,7 @@ where
impl<'a, Message, Renderer, I> Widget<Message, Renderer>
for RectangleTrackingContainer<'a, Message, Renderer, I>
where
Renderer: iced_native::Renderer,
Renderer: iced_core::Renderer,
Renderer::Theme: StyleSheet,
I: 'a + Hash + Copy + Send + Sync + Debug,
{
@ -152,7 +152,7 @@ where
self.container.children()
}
fn diff(&self, tree: &mut Tree) {
fn diff(&mut self, tree: &mut Tree) {
self.container.diff(tree);
}
@ -168,8 +168,16 @@ where
self.container.layout(renderer, limits)
}
fn operate(&self, tree: &mut Tree, layout: Layout<'_>, operation: &mut dyn Operation<Message>) {
self.container.operate(tree, layout, operation);
fn operate(
&self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<
iced_core::widget::OperationOutputWrapper<Message>,
>,
) {
self.container.operate(tree, layout, renderer, operation);
}
fn on_event(
@ -229,7 +237,7 @@ where
}
fn overlay<'b>(
&'b self,
&'b mut self,
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
@ -242,7 +250,7 @@ impl<'a, Message, Renderer, I> From<RectangleTrackingContainer<'a, Message, Rend
for Element<'a, Message, Renderer>
where
Message: 'a,
Renderer: 'a + iced_native::Renderer,
Renderer: 'a + iced_core::Renderer,
Renderer::Theme: StyleSheet,
I: 'a + Hash + Copy + Send + Sync + Debug,
{

View file

@ -26,44 +26,51 @@ pub enum State<I> {
async fn start_listening<I: Copy, R: 'static + Hash + Copy + Send + Sync + Debug + Eq>(
id: I,
state: State<R>,
) -> (Option<(I, RectangleUpdate<R>)>, State<R>) {
match state {
State::Ready => {
let (tx, rx) = unbounded();
mut state: State<R>,
) -> ((I, RectangleUpdate<R>), State<R>) {
loop {
let (update, new_state) = match state {
State::Ready => {
let (tx, rx) = unbounded();
(
Some((id, RectangleUpdate::Init(RectangleTracker { tx }))),
State::Waiting(rx, HashMap::new()),
)
}
State::Waiting(mut rx, mut map) => match rx.next().await {
Some(u) => {
if let Some(prev) = map.get(&u.0) {
let new = u.1;
if prev.width != new.width
|| prev.height != new.height
|| prev.x != new.x
|| prev.y != new.y
{
map.insert(u.0, new);
return (
(
Some((id, RectangleUpdate::Init(RectangleTracker { tx }))),
State::Waiting(rx, HashMap::new()),
)
}
State::Waiting(mut rx, mut map) => match rx.next().await {
Some(u) => {
if let Some(prev) = map.get(&u.0) {
let new = u.1;
if (prev.width - new.width).abs() > 0.1
|| (prev.height - new.height).abs() > 0.1
|| (prev.x - new.x).abs() > 0.1
|| (prev.y - new.y).abs() > 0.1
{
map.insert(u.0, new);
(
Some((id, RectangleUpdate::Rectangle(u))),
State::Waiting(rx, map),
)
} else {
(None, State::Waiting(rx, map))
}
} else {
map.insert(u.0, u.1);
(
Some((id, RectangleUpdate::Rectangle(u))),
State::Waiting(rx, map),
);
)
}
} else {
map.insert(u.0, u.1);
return (
Some((id, RectangleUpdate::Rectangle(u))),
State::Waiting(rx, map),
);
}
(None, State::Waiting(rx, map))
}
None => (None, State::Finished),
},
State::Finished => iced::futures::future::pending().await,
None => (None, State::Finished),
},
State::Finished => iced::futures::future::pending().await,
};
state = new_state;
if let Some(u) = update {
return (u, state);
}
}
}

View file

@ -8,6 +8,6 @@ pub fn scrollable<'a, Message>(
element: impl Into<Element<'a, Message>>,
) -> widget::Scrollable<'a, Message, Renderer> {
widget::scrollable(element)
.scrollbar_width(8)
.scroller_width(8)
// .scrollbar_width(8) TODO add these back
// .scroller_width(8)
}

View file

@ -11,7 +11,7 @@ use apply::Apply;
/// A search field for COSMIC applications.
pub fn field<Message: 'static + Clone>(
id: iced::widget::text_input::Id,
id: iced_core::id::Id,
phrase: &str,
on_change: fn(String) -> Message,
on_clear: Message,
@ -29,7 +29,7 @@ pub fn field<Message: 'static + Clone>(
/// A search field for COSMIC applications.
#[must_use]
pub struct Field<'a, Message: 'static + Clone> {
id: iced::widget::text_input::Id,
id: iced_core::id::Id,
phrase: &'a str,
on_change: fn(String) -> Message,
on_clear: Message,
@ -38,7 +38,8 @@ pub struct Field<'a, Message: 'static + Clone> {
impl<'a, Message: 'static + Clone> Field<'a, Message> {
pub fn into_element(mut self) -> crate::Element<'a, Message> {
let mut input = iced::widget::text_input("", self.phrase, self.on_change)
let mut input = iced::widget::text_input("", self.phrase)
.on_input(self.on_change)
.style(crate::theme::TextInput::Search)
.width(Length::Fill)
.id(self.id);
@ -52,8 +53,8 @@ impl<'a, Message: 'static + Clone> Field<'a, Message> {
input,
clear_button().on_press(self.on_clear)
)
.width(Length::Units(300))
.height(Length::Units(38))
.width(Length::Fixed(300.0))
.height(Length::Fixed(38.0))
.padding([0, 16])
.spacing(8)
.align_items(iced::Alignment::Center)
@ -84,7 +85,7 @@ fn active_style(theme: &crate::Theme) -> container::Appearance {
iced::widget::container::Appearance {
text_color: Some(cosmic.palette.neutral_9.into()),
background: Some(Background::Color(neutral_7.into())),
border_radius: 24.0,
border_radius: 24.0.into(),
border_width: 2.0,
border_color: cosmic.accent.focus.into(),
}

View file

@ -6,14 +6,13 @@ use crate::iced;
/// A model for managing the state of a search widget.
pub struct Model {
pub input_id: iced::widget::text_input::Id,
pub input_id: iced_core::id::Id,
pub phrase: String,
pub state: State,
}
impl Model {
/// Focuses the search field.
#[must_use]
pub fn focus<Message: 'static>(&mut self) -> crate::iced::Command<Message> {
self.state = State::Active;
iced::widget::text_input::focus(self.input_id.clone())
@ -29,7 +28,7 @@ impl Model {
impl Default for Model {
fn default() -> Self {
Self {
input_id: iced::widget::text_input::Id::unique(),
input_id: iced_core::id::Id::unique(),
phrase: String::with_capacity(32),
state: State::Inactive,
}

View file

@ -8,7 +8,7 @@ use super::style::StyleSheet;
use super::widget::{SegmentedButton, SegmentedVariant};
use iced::{Length, Rectangle, Size};
use iced_native::layout;
use iced_core::layout;
/// Horizontal [`SegmentedButton`].
pub type HorizontalSegmentedButton<'a, SelectionMode, Message, Renderer> =
@ -25,10 +25,10 @@ pub fn horizontal<SelectionMode: Default, Message, Renderer>(
model: &Model<SelectionMode>,
) -> SegmentedButton<Horizontal, SelectionMode, Message, Renderer>
where
Renderer: iced_native::Renderer
+ iced_native::text::Renderer
+ iced_native::image::Renderer
+ iced_native::svg::Renderer,
Renderer: iced_core::Renderer
+ iced_core::text::Renderer
+ iced_core::image::Renderer
+ iced_core::svg::Renderer,
Renderer::Theme: StyleSheet,
Model<SelectionMode>: Selectable,
{
@ -38,10 +38,10 @@ where
impl<'a, SelectionMode, Message, Renderer> SegmentedVariant
for SegmentedButton<'a, Horizontal, SelectionMode, Message, Renderer>
where
Renderer: iced_native::Renderer
+ iced_native::text::Renderer
+ iced_native::image::Renderer
+ iced_native::svg::Renderer,
Renderer: iced_core::Renderer
+ iced_core::text::Renderer
+ iced_core::image::Renderer
+ iced_core::svg::Renderer,
Renderer::Theme: StyleSheet,
Model<SelectionMode>: Selectable,
SelectionMode: Default,
@ -49,8 +49,8 @@ where
type Renderer = Renderer;
fn variant_appearance(
theme: &<Self::Renderer as iced_native::Renderer>::Theme,
style: &<<Self::Renderer as iced_native::Renderer>::Theme as StyleSheet>::Style,
theme: &<Self::Renderer as iced_core::Renderer>::Theme,
style: &<<Self::Renderer as iced_core::Renderer>::Theme as StyleSheet>::Style,
) -> super::Appearance {
theme.horizontal(style)
}
@ -85,7 +85,7 @@ where
}
let size = limits
.height(Length::Units(height as u16))
.height(Length::Fixed(height))
.resolve(Size::new(width, height));
layout::Node::new(size)

View file

@ -1,7 +1,7 @@
// Copyright 2022 System76 <info@system76.com>
// SPDX-License-Identifier: MPL-2.0
use iced_core::{Background, BorderRadius, Color};
use iced_core::{renderer::BorderRadius, Background, Color};
/// Appearance of the segmented button.
#[derive(Default, Clone, Copy)]

View file

@ -8,7 +8,7 @@ use super::style::StyleSheet;
use super::widget::{SegmentedButton, SegmentedVariant};
use iced::{Length, Rectangle, Size};
use iced_native::layout;
use iced_core::layout;
/// A type marker defining the vertical variant of a [`SegmentedButton`].
pub struct Vertical;
@ -25,10 +25,10 @@ pub fn vertical<SelectionMode, Message, Renderer>(
model: &Model<SelectionMode>,
) -> SegmentedButton<Vertical, SelectionMode, Message, Renderer>
where
Renderer: iced_native::Renderer
+ iced_native::text::Renderer
+ iced_native::image::Renderer
+ iced_native::svg::Renderer,
Renderer: iced_core::Renderer
+ iced_core::text::Renderer
+ iced_core::image::Renderer
+ iced_core::svg::Renderer,
Renderer::Theme: StyleSheet,
Model<SelectionMode>: Selectable,
SelectionMode: Default,
@ -39,10 +39,10 @@ where
impl<'a, SelectionMode, Message, Renderer> SegmentedVariant
for SegmentedButton<'a, Vertical, SelectionMode, Message, Renderer>
where
Renderer: iced_native::Renderer
+ iced_native::text::Renderer
+ iced_native::image::Renderer
+ iced_native::svg::Renderer,
Renderer: iced_core::Renderer
+ iced_core::text::Renderer
+ iced_core::image::Renderer
+ iced_core::svg::Renderer,
Renderer::Theme: StyleSheet,
Model<SelectionMode>: Selectable,
SelectionMode: Default,
@ -50,8 +50,8 @@ where
type Renderer = Renderer;
fn variant_appearance(
theme: &<Self::Renderer as iced_native::Renderer>::Theme,
style: &<<Self::Renderer as iced_native::Renderer>::Theme as StyleSheet>::Style,
theme: &<Self::Renderer as iced_core::Renderer>::Theme,
style: &<<Self::Renderer as iced_core::Renderer>::Theme as StyleSheet>::Style,
) -> super::Appearance {
theme.vertical(style)
}
@ -86,7 +86,7 @@ where
}
let size = limits
.height(Length::Units(height as u16))
.height(Length::Fixed(height))
.resolve(Size::new(width, height));
layout::Node::new(size)

View file

@ -10,9 +10,10 @@ use iced::{
alignment, event, keyboard, mouse, touch, Background, Color, Command, Element, Event, Length,
Point, Rectangle, Size,
};
use iced_core::BorderRadius;
use iced_native::widget::{self, operation, tree, Operation};
use iced_native::{layout, renderer, widget::Tree, Clipboard, Layout, Shell, Widget};
use iced_core::renderer::BorderRadius;
use iced_core::text::{LineHeight, Shaping};
use iced_core::widget::{self, operation, tree};
use iced_core::{layout, renderer, widget::Tree, Clipboard, Layout, Shell, Widget};
use std::marker::PhantomData;
/// State that is maintained by each individual widget.
@ -46,15 +47,15 @@ impl operation::Focusable for LocalState {
/// Isolates variant-specific behaviors from [`SegmentedButton`].
pub trait SegmentedVariant {
type Renderer: iced_native::Renderer;
type Renderer: iced_core::Renderer;
/// Get the appearance for this variant of the widget.
fn variant_appearance(
theme: &<Self::Renderer as iced_native::Renderer>::Theme,
style: &<<Self::Renderer as iced_native::Renderer>::Theme as StyleSheet>::Style,
theme: &<Self::Renderer as iced_core::Renderer>::Theme,
style: &<<Self::Renderer as iced_core::Renderer>::Theme as StyleSheet>::Style,
) -> super::Appearance
where
<Self::Renderer as iced_native::Renderer>::Theme: StyleSheet;
<Self::Renderer as iced_core::Renderer>::Theme: StyleSheet;
/// Calculates the bounds for the given button by its position.
fn variant_button_bounds(&self, bounds: Rectangle, position: usize) -> Rectangle;
@ -67,10 +68,10 @@ pub trait SegmentedVariant {
#[derive(Setters)]
pub struct SegmentedButton<'a, Variant, SelectionMode, Message, Renderer>
where
Renderer: iced_native::Renderer
+ iced_native::text::Renderer
+ iced_native::image::Renderer
+ iced_native::svg::Renderer,
Renderer: iced_core::Renderer
+ iced_core::text::Renderer
+ iced_core::image::Renderer
+ iced_core::svg::Renderer,
Renderer::Theme: StyleSheet,
Model<SelectionMode>: Selectable,
SelectionMode: Default,
@ -91,13 +92,13 @@ where
/// Spacing between icon and text in button.
pub(super) button_spacing: u16,
/// Desired font for active tabs.
pub(super) font_active: Renderer::Font,
pub(super) font_active: Option<Renderer::Font>,
/// Desired font for hovered tabs.
pub(super) font_hovered: Renderer::Font,
pub(super) font_hovered: Option<Renderer::Font>,
/// Desired font for inactive tabs.
pub(super) font_inactive: Renderer::Font,
pub(super) font_inactive: Option<Renderer::Font>,
/// Size of the font.
pub(super) font_size: u16,
pub(super) font_size: f32,
/// Size of icon
pub(super) icon_size: u16,
/// Desired width of the widget.
@ -106,6 +107,8 @@ where
pub(super) height: Length,
/// Desired spacing between items.
pub(super) spacing: u16,
/// LineHeight of the font.
pub(super) line_height: LineHeight,
/// Style to draw the widget in.
#[setters(into)]
pub(super) style: <Renderer::Theme as StyleSheet>::Style,
@ -122,10 +125,10 @@ where
impl<'a, Variant, SelectionMode, Message, Renderer>
SegmentedButton<'a, Variant, SelectionMode, Message, Renderer>
where
Renderer: iced_native::Renderer
+ iced_native::text::Renderer
+ iced_native::image::Renderer
+ iced_native::svg::Renderer,
Renderer: iced_core::Renderer
+ iced_core::text::Renderer
+ iced_core::image::Renderer
+ iced_core::svg::Renderer,
Renderer::Theme: StyleSheet,
Self: SegmentedVariant<Renderer = Renderer>,
Model<SelectionMode>: Selectable,
@ -141,14 +144,15 @@ where
button_padding: [4, 4, 4, 4],
button_height: 32,
button_spacing: 4,
font_active: Renderer::Font::default(),
font_hovered: Renderer::Font::default(),
font_inactive: Renderer::Font::default(),
font_size: 17,
font_active: None,
font_hovered: None,
font_inactive: None,
font_size: 17.0,
icon_size: 16,
height: Length::Shrink,
width: Length::Fill,
spacing: 0,
line_height: LineHeight::default(),
style: <Renderer::Theme as StyleSheet>::Style::default(),
on_activate: None,
on_close: None,
@ -212,6 +216,7 @@ where
pub(super) fn max_button_dimensions(&self, renderer: &Renderer, bounds: Size) -> (f32, f32) {
let mut width = 0.0f32;
let mut height = 0.0f32;
let font = renderer.default_font();
for key in self.model.order.iter().copied() {
let mut button_width = 0.0f32;
@ -219,7 +224,14 @@ where
// Add text to measurement if text was given.
if let Some(text) = self.model.text(key) {
let (w, h) = renderer.measure(text, self.font_size, Default::default(), bounds);
let (w, h) = renderer.measure(
text,
self.font_size,
self.line_height,
font,
bounds,
Shaping::Advanced,
);
button_width = w;
button_height = h;
@ -253,10 +265,10 @@ where
impl<'a, Variant, SelectionMode, Message, Renderer> Widget<Message, Renderer>
for SegmentedButton<'a, Variant, SelectionMode, Message, Renderer>
where
Renderer: iced_native::Renderer
+ iced_native::text::Renderer
+ iced_native::image::Renderer
+ iced_native::svg::Renderer,
Renderer: iced_core::Renderer
+ iced_core::text::Renderer
+ iced_core::image::Renderer
+ iced_core::svg::Renderer,
Renderer::Theme: StyleSheet,
Self: SegmentedVariant<Renderer = Renderer>,
Model<SelectionMode>: Selectable,
@ -379,7 +391,10 @@ where
&self,
tree: &mut Tree,
_layout: Layout<'_>,
operation: &mut dyn Operation<Message>,
_renderer: &Renderer,
operation: &mut dyn iced_core::widget::Operation<
iced_core::widget::OperationOutputWrapper<Message>,
>,
) {
let state = tree.state.downcast_mut::<LocalState>();
operation.focusable(state, self.id.as_ref().map(|id| &id.0));
@ -392,7 +407,7 @@ where
cursor_position: iced::Point,
_viewport: &iced::Rectangle,
_renderer: &Renderer,
) -> iced_native::mouse::Interaction {
) -> iced_core::mouse::Interaction {
let bounds = layout.bounds();
if bounds.contains(cursor_position) {
@ -402,15 +417,15 @@ where
.contains(cursor_position)
{
return if self.model.items[key].enabled {
iced_native::mouse::Interaction::Pointer
iced_core::mouse::Interaction::Pointer
} else {
iced_native::mouse::Interaction::Idle
iced_core::mouse::Interaction::Idle
};
}
}
}
iced_native::mouse::Interaction::Idle
iced_core::mouse::Interaction::Idle
}
#[allow(clippy::too_many_lines)]
@ -418,7 +433,7 @@ where
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &<Renderer as iced_native::Renderer>::Theme,
theme: &<Renderer as iced_core::Renderer>::Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor_position: iced::Point,
@ -458,6 +473,7 @@ where
} else {
(appearance.inactive, &self.font_inactive)
};
let font = font.unwrap_or_else(|| renderer.default_font());
let button_appearance = if nth == 0 {
status_appearance.first
@ -536,7 +552,7 @@ where
unimplemented!()
}
icon::Handle::Svg(handle) => {
iced_native::svg::Renderer::draw(renderer, handle, icon_color, icon_bounds);
iced_core::svg::Renderer::draw(renderer, handle, icon_color, icon_bounds);
}
}
@ -550,14 +566,16 @@ where
bounds.y = y;
// Draw the text in this button.
renderer.fill_text(iced_native::text::Text {
renderer.fill_text(iced_core::text::Text {
content: text,
size: f32::from(self.font_size),
size: self.font_size,
bounds,
color: status_appearance.text_color,
font: font.clone(),
font,
horizontal_alignment,
vertical_alignment: alignment::Vertical::Center,
shaping: Shaping::Advanced,
line_height: self.line_height,
});
}
@ -575,7 +593,7 @@ where
unimplemented!()
}
icon::Handle::Svg(handle) => {
iced_native::svg::Renderer::draw(
iced_core::svg::Renderer::draw(
renderer,
handle,
Some(status_appearance.text_color),
@ -588,11 +606,11 @@ where
}
fn overlay<'b>(
&'b self,
&'b mut self,
_tree: &'b mut Tree,
_layout: iced_native::Layout<'_>,
_layout: iced_core::Layout<'_>,
_renderer: &Renderer,
) -> Option<iced_native::overlay::Element<'b, Message, Renderer>> {
) -> Option<iced_core::overlay::Element<'b, Message, Renderer>> {
None
}
}
@ -601,10 +619,10 @@ impl<'a, Variant, SelectionMode, Message, Renderer>
From<SegmentedButton<'a, Variant, SelectionMode, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: iced_native::Renderer
+ iced_native::text::Renderer
+ iced_native::image::Renderer
+ iced_native::svg::Renderer
Renderer: iced_core::Renderer
+ iced_core::text::Renderer
+ iced_core::image::Renderer
+ iced_core::svg::Renderer
+ 'a,
Renderer::Theme: StyleSheet,
SegmentedButton<'a, Variant, SelectionMode, Message, Renderer>:
@ -624,7 +642,6 @@ where
}
/// A command that focuses a segmented item stored in a widget.
#[must_use]
pub fn focus<Message: 'static>(id: Id) -> Command<Message> {
Command::widget(operation::focusable::focus(id.0))
}

View file

@ -25,7 +25,7 @@ where
.button_padding([16, 0, 16, 0])
.button_height(32)
.style(crate::theme::SegmentedButton::Selection)
.font_active(crate::font::FONT_SEMIBOLD)
.font_active(Some(crate::font::FONT_SEMIBOLD))
}
/// A selection of multiple choices appearing as a conjoined button.
@ -45,5 +45,5 @@ where
.button_padding([16, 0, 16, 0])
.button_height(32)
.style(crate::theme::SegmentedButton::Selection)
.font_active(crate::font::FONT_SEMIBOLD)
.font_active(Some(crate::font::FONT_SEMIBOLD))
}

View file

@ -46,43 +46,41 @@ impl<'a, Message: 'static> SpinButton<'a, Message> {
icon("list-remove-symbolic", 24)
.style(theme::Svg::Symbolic)
.apply(container)
.width(Length::Fill)
.height(Length::Fill)
.width(Length::Fixed(32.0))
.height(Length::Fixed(32.0))
.align_x(Horizontal::Center)
.align_y(Vertical::Center)
.apply(button)
.width(Length::Fill)
.height(Length::Fill)
.width(Length::Fixed(32.0))
.height(Length::Fixed(32.0))
.style(theme::Button::Text)
.on_press(model::Message::Decrement),
text(label)
.vertical_alignment(Vertical::Center)
.apply(container)
.width(Length::Fill)
.height(Length::Fill)
.align_x(Horizontal::Center)
.align_y(Vertical::Center),
icon("list-add-symbolic", 24)
.style(theme::Svg::Symbolic)
.apply(container)
.width(Length::Fill)
.height(Length::Fill)
.width(Length::Fixed(32.0))
.height(Length::Fixed(32.0))
.align_x(Horizontal::Center)
.align_y(Vertical::Center)
.apply(button)
.width(Length::Fill)
.height(Length::Fill)
.width(Length::Fixed(32.0))
.height(Length::Fixed(32.0))
.style(theme::Button::Text)
.on_press(model::Message::Increment),
]
.width(Length::Fill)
.height(Length::Units(32))
.width(Length::Shrink)
.height(Length::Fixed(32.0))
.spacing(4.0)
.align_items(Alignment::Center),
)
.padding([4, 4])
.align_y(Vertical::Center)
.width(Length::Units(95))
.height(Length::Units(32))
.width(Length::Shrink)
.height(Length::Fixed(32.0))
.style(theme::Container::custom(container_style))
.apply(Element::from)
.map(on_change)
@ -104,7 +102,7 @@ fn container_style(theme: &crate::Theme) -> iced_style::container::Appearance {
iced_style::container::Appearance {
text_color: Some(basic.palette.neutral_10.into()),
background: Some(Background::Color(neutral_10.into())),
border_radius: 24.0,
border_radius: 24.0.into(),
border_width: 0.0,
border_color: accent.base.into(),
}

View file

@ -7,7 +7,7 @@ pub use iced::widget::Text;
/// [`Text`]: widget::Text
pub fn text<'a, Renderer>(text: impl Into<Cow<'a, str>>) -> Text<'a, Renderer>
where
Renderer: iced_native::text::Renderer,
Renderer: iced_core::text::Renderer,
Renderer::Theme: iced::widget::text::StyleSheet,
{
Text::new(text)

View file

@ -9,7 +9,7 @@ pub fn toggler<'a, Message>(
is_checked: bool,
f: impl Fn(bool) -> Message + 'a,
) -> widget::Toggler<'a, Message, Renderer> {
widget::Toggler::new(is_checked, label, f)
widget::Toggler::new(label, is_checked, f)
.size(24)
.spacing(12)
.width(Length::Shrink)

View file

@ -25,7 +25,7 @@ where
.button_padding([16, 0, 16, 0])
.button_height(48)
.style(crate::theme::SegmentedButton::ViewSwitcher)
.font_active(crate::font::FONT_SEMIBOLD)
.font_active(Some(crate::font::FONT_SEMIBOLD))
}
/// A collection of tabs for developing a tabbed interface.
@ -45,5 +45,5 @@ where
.button_padding([16, 0, 16, 0])
.button_height(48)
.style(crate::theme::SegmentedButton::ViewSwitcher)
.font_active(crate::font::FONT_SEMIBOLD)
.font_active(Some(crate::font::FONT_SEMIBOLD))
}

View file

@ -64,11 +64,12 @@ impl<'a, Message: 'static + Clone> From<Warning<'a, Message>> for Element<'a, Me
}
}
#[must_use]
pub fn warning_container(theme: &Theme) -> widget::container::Appearance {
widget::container::Appearance {
text_color: Some(theme.cosmic().warning.on.into()),
background: Some(Background::Color(theme.cosmic().warning_color().into())),
border_radius: 0.0,
border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
}