chore: updates after iced rebase
This commit is contained in:
parent
7bb5ae7cfe
commit
a48c4fc47d
16 changed files with 753 additions and 736 deletions
937
Cargo.lock
generated
937
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
14
Cargo.toml
14
Cargo.toml
|
|
@ -20,14 +20,18 @@ cosmic-comp-config = { path = "cosmic-comp-config", features = [
|
|||
cosmic-config = { git = "https://github.com/pop-os/libcosmic/", features = [
|
||||
"calloop",
|
||||
"macro",
|
||||
] }
|
||||
], branch = "iced-rebase" }
|
||||
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", rev = "160b086", default-features = false, features = [
|
||||
"server",
|
||||
] }
|
||||
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon" }
|
||||
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", branch = "iced-rebase" }
|
||||
cosmic-settings-daemon-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", features = [
|
||||
"greeter",
|
||||
] }
|
||||
], branch = "iced-rebase" }
|
||||
# cosmic-settings-config = { path = "../cosmic-settings-daemon/config" }
|
||||
# cosmic-settings-daemon-config = { path = "../cosmic-settings-daemon/cosmic-settings-daemon-config", features = [
|
||||
# "greeter",
|
||||
# ] }
|
||||
cosmic-text = { git = "https://github.com/pop-os/cosmic-text.git", features = [
|
||||
"shape-run-cache",
|
||||
] }
|
||||
|
|
@ -39,11 +43,11 @@ i18n-embed = { version = "0.16", features = [
|
|||
"desktop-requester",
|
||||
] }
|
||||
i18n-embed-fl = "0.10"
|
||||
iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic/" }
|
||||
iced_tiny_skia = { git = "https://github.com/pop-os/libcosmic", branch = "iced-rebase" }
|
||||
indexmap = "2.13"
|
||||
keyframe = "1.1.1"
|
||||
libc = "0.2.182"
|
||||
libcosmic = { git = "https://github.com/pop-os/libcosmic/", default-features = false }
|
||||
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, branch = "iced-rebase" }
|
||||
libsystemd = { version = "0.7", optional = true }
|
||||
log-panics = { version = "2", features = ["with-backtrace"] }
|
||||
ordered-float = "5.1"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ version = "1.0.0"
|
|||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic/" }
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic", branch = "iced-rebase" }
|
||||
cosmic-randr-shell = { git = "https://github.com/pop-os/cosmic-randr/", optional = true }
|
||||
input = "0.9.1"
|
||||
libdisplay-info = { version = "0.3.0", optional = true }
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use cosmic::{
|
|||
Apply,
|
||||
iced::{
|
||||
Alignment,
|
||||
widget::{column, container, horizontal_space, row, vertical_space},
|
||||
widget::{column, container, row, space},
|
||||
},
|
||||
iced_core::{Background, Border, Color, Length},
|
||||
theme,
|
||||
|
|
@ -70,6 +70,7 @@ impl Program for ResizeIndicatorInternal {
|
|||
let edges = self.edges.lock().unwrap();
|
||||
let icon_container_style = || {
|
||||
theme::Container::custom(|theme| container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
||||
|
|
@ -99,7 +100,7 @@ impl Program for ResizeIndicatorInternal {
|
|||
.center_x(Length::Fill)
|
||||
.into()
|
||||
} else {
|
||||
vertical_space().height(36).into()
|
||||
space::vertical().height(36).into()
|
||||
},
|
||||
row(vec![
|
||||
if edges.contains(ResizeEdge::LEFT) {
|
||||
|
|
@ -118,12 +119,12 @@ impl Program for ResizeIndicatorInternal {
|
|||
.center_y(Length::Fill)
|
||||
.into()
|
||||
} else {
|
||||
horizontal_space().width(36).into()
|
||||
space::horizontal().width(36).into()
|
||||
},
|
||||
row(vec![
|
||||
text::heading(&self.shortcut1).into(),
|
||||
text::body(fl!("grow-window")).into(),
|
||||
horizontal_space().width(40).into(),
|
||||
space::horizontal().width(40).into(),
|
||||
text::heading(&self.shortcut2).into(),
|
||||
text::body(fl!("shrink-window")).into(),
|
||||
])
|
||||
|
|
@ -155,7 +156,7 @@ impl Program for ResizeIndicatorInternal {
|
|||
.center_y(Length::Fill)
|
||||
.into()
|
||||
} else {
|
||||
horizontal_space().width(36).into()
|
||||
space::horizontal().width(36).into()
|
||||
},
|
||||
])
|
||||
.width(Length::Fill)
|
||||
|
|
@ -177,7 +178,7 @@ impl Program for ResizeIndicatorInternal {
|
|||
.center_x(Length::Fill)
|
||||
.into()
|
||||
} else {
|
||||
vertical_space().height(36).into()
|
||||
space::vertical().height(36).into()
|
||||
},
|
||||
])
|
||||
.into()
|
||||
|
|
|
|||
|
|
@ -1303,7 +1303,7 @@ impl Decorations<CosmicStackInternal, Message> for DefaultDecorations {
|
|||
.height(Length::Fill)
|
||||
.width(Length::Fill),
|
||||
),
|
||||
iced_widget::horizontal_space()
|
||||
iced_widget::space::horizontal()
|
||||
.width(Length::Fixed(0.0))
|
||||
.apply(iced_widget::container)
|
||||
.padding([64, 24])
|
||||
|
|
@ -1345,6 +1345,7 @@ impl Decorations<CosmicStackInternal, Message> for DefaultDecorations {
|
|||
};
|
||||
|
||||
iced_widget::container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(cosmic_theme.background.on.into()),
|
||||
text_color: Some(cosmic_theme.background.on.into()),
|
||||
background: Some(Background::Color(background.into())),
|
||||
|
|
|
|||
|
|
@ -30,19 +30,19 @@ impl From<TabRuleTheme> for theme::Rule {
|
|||
match theme {
|
||||
TabRuleTheme::ActiveActivated => Self::custom(|theme| widget::rule::Style {
|
||||
color: theme.cosmic().accent_color().into(),
|
||||
width: 4,
|
||||
snap: true,
|
||||
radius: 0.0.into(),
|
||||
fill_mode: FillMode::Full,
|
||||
}),
|
||||
TabRuleTheme::ActiveDeactivated => Self::custom(|theme| widget::rule::Style {
|
||||
color: theme.cosmic().palette.neutral_5.into(),
|
||||
width: 4,
|
||||
snap: true,
|
||||
radius: 0.0.into(),
|
||||
fill_mode: FillMode::Full,
|
||||
}),
|
||||
TabRuleTheme::Default => Self::custom(|theme| widget::rule::Style {
|
||||
color: theme.cosmic().palette.neutral_5.into(),
|
||||
width: 4,
|
||||
snap: true,
|
||||
radius: 8.0.into(),
|
||||
fill_mode: FillMode::Padded(4),
|
||||
}),
|
||||
|
|
@ -62,6 +62,7 @@ impl From<TabBackgroundTheme> for theme::Container<'_> {
|
|||
match background_theme {
|
||||
TabBackgroundTheme::ActiveActivated => {
|
||||
Self::custom(move |theme| widget::container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(Color::from(theme.cosmic().accent_text_color())),
|
||||
text_color: Some(Color::from(theme.cosmic().accent_text_color())),
|
||||
background: Some(Background::Color(
|
||||
|
|
@ -77,6 +78,7 @@ impl From<TabBackgroundTheme> for theme::Container<'_> {
|
|||
}
|
||||
TabBackgroundTheme::ActiveDeactivated => {
|
||||
Self::custom(move |theme| widget::container::Style {
|
||||
snap: true,
|
||||
icon_color: None,
|
||||
text_color: None,
|
||||
background: Some(Background::Color(
|
||||
|
|
@ -186,7 +188,7 @@ impl<Message: TabMessage + 'static> Tab<Message> {
|
|||
}
|
||||
|
||||
let items = vec![
|
||||
widget::vertical_rule(4).class(self.rule_theme).into(),
|
||||
widget::rule::vertical(4).class(self.rule_theme).into(),
|
||||
self.app_icon
|
||||
.clone()
|
||||
.apply(widget::container)
|
||||
|
|
@ -262,7 +264,7 @@ where
|
|||
Size::new(Length::Fill, Length::Fill)
|
||||
}
|
||||
|
||||
fn layout(&self, tree: &mut Tree, renderer: &cosmic::Renderer, limits: &Limits) -> Node {
|
||||
fn layout(&mut self, tree: &mut Tree, renderer: &cosmic::Renderer, limits: &Limits) -> Node {
|
||||
let min_size = Size {
|
||||
height: TAB_HEIGHT as f32,
|
||||
width: if self.active {
|
||||
|
|
@ -295,74 +297,68 @@ where
|
|||
8.,
|
||||
cosmic::iced::Alignment::Center,
|
||||
if size.width >= CLOSE_BREAKPOINT as f32 {
|
||||
&self.elements
|
||||
&mut self.elements
|
||||
} else if size.width >= TEXT_BREAKPOINT as f32 {
|
||||
&self.elements[0..3]
|
||||
&mut self.elements[0..3]
|
||||
} else {
|
||||
&self.elements[0..2]
|
||||
&mut self.elements[0..2]
|
||||
},
|
||||
&mut tree.children,
|
||||
)
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &cosmic::Renderer,
|
||||
operation: &mut dyn Operation<()>,
|
||||
) {
|
||||
operation.container(None, layout.bounds(), &mut |operation| {
|
||||
operation.container(None, layout.bounds());
|
||||
operation.traverse(&mut |operation| {
|
||||
self.elements
|
||||
.iter()
|
||||
.iter_mut()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.for_each(|((child, state), layout)| {
|
||||
child
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.operate(state, layout, renderer, operation);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
fn update(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: event::Event,
|
||||
event: &event::Event,
|
||||
layout: Layout<'_>,
|
||||
cursor: mouse::Cursor,
|
||||
renderer: &cosmic::Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
viewport: &Rectangle,
|
||||
) -> event::Status {
|
||||
) {
|
||||
let status = self
|
||||
.elements
|
||||
.iter_mut()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.map(|((child, state), layout)| {
|
||||
child.as_widget_mut().on_event(
|
||||
state,
|
||||
event.clone(),
|
||||
layout,
|
||||
cursor,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
viewport,
|
||||
child.as_widget_mut().update(
|
||||
state, event, layout, cursor, renderer, clipboard, shell, viewport,
|
||||
)
|
||||
})
|
||||
.fold(event::Status::Ignored, event::Status::merge);
|
||||
});
|
||||
|
||||
if status == event::Status::Ignored && cursor.is_over(layout.bounds()) {
|
||||
if !shell.is_event_captured() && cursor.is_over(layout.bounds()) {
|
||||
if matches!(
|
||||
event,
|
||||
event::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
||||
) {
|
||||
if let Some(message) = self.press_message.clone() {
|
||||
shell.publish(message);
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if matches!(
|
||||
|
|
@ -371,7 +367,8 @@ where
|
|||
) {
|
||||
if let Some(message) = self.right_click_message.clone() {
|
||||
shell.publish(message);
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if matches!(
|
||||
|
|
@ -379,11 +376,10 @@ where
|
|||
event::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
||||
) {
|
||||
shell.publish(Message::activate(self.idx));
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
status
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
|
|
@ -447,10 +443,18 @@ where
|
|||
fn overlay<'b>(
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
layout: Layout<'b>,
|
||||
renderer: &cosmic::Renderer,
|
||||
viewport: &Rectangle,
|
||||
translation: cosmic::iced::Vector,
|
||||
) -> Option<overlay::Element<'b, Message, cosmic::Theme, cosmic::Renderer>> {
|
||||
overlay::from_children(&mut self.elements, tree, layout, renderer, translation)
|
||||
overlay::from_children(
|
||||
&mut self.elements,
|
||||
tree,
|
||||
layout,
|
||||
renderer,
|
||||
viewport,
|
||||
translation,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,10 +78,10 @@ impl TabText {
|
|||
<cosmic::Renderer as TextRenderer>::Paragraph::with_text(Text {
|
||||
content: &self.text,
|
||||
size: cosmic::iced_core::Pixels(self.font_size),
|
||||
bounds: Size::INFINITY,
|
||||
bounds: Size::INFINITE,
|
||||
font: self.font,
|
||||
horizontal_alignment: alignment::Horizontal::Left,
|
||||
vertical_alignment: alignment::Vertical::Center,
|
||||
align_x: cosmic::iced_core::text::Alignment::Left,
|
||||
align_y: alignment::Vertical::Center,
|
||||
shaping: Shaping::Advanced,
|
||||
line_height: LineHeight::default(),
|
||||
wrapping: Wrapping::None,
|
||||
|
|
@ -107,7 +107,7 @@ impl<Message> Widget<Message, cosmic::Theme, cosmic::Renderer> for TabText {
|
|||
Size::new(self.width, self.height)
|
||||
}
|
||||
|
||||
fn layout(&self, tree: &mut Tree, _renderer: &cosmic::Renderer, limits: &Limits) -> Node {
|
||||
fn layout(&mut self, tree: &mut Tree, _renderer: &cosmic::Renderer, limits: &Limits) -> Node {
|
||||
let state = tree.state.downcast_mut::<LocalState>();
|
||||
let text_bounds = state.paragraph.min_bounds();
|
||||
state.overflowed = limits.max().width < text_bounds.width;
|
||||
|
|
@ -166,6 +166,7 @@ impl<Message> Widget<Message, cosmic::Theme, cosmic::Renderer> for TabText {
|
|||
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
snap: true,
|
||||
bounds: Rectangle {
|
||||
x: (bounds.x + bounds.width - 24.).max(bounds.x),
|
||||
width: 24.0_f32.min(bounds.width),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use crate::backend::render::element;
|
||||
|
||||
use super::tab::{MIN_ACTIVE_TAB_WIDTH, Tab, TabBackgroundTheme, TabMessage, TabRuleTheme};
|
||||
use cosmic::{
|
||||
Apply,
|
||||
|
|
@ -64,12 +66,12 @@ pub struct State {
|
|||
}
|
||||
|
||||
impl Scrollable for State {
|
||||
fn snap_to(&mut self, offset: RelativeOffset) {
|
||||
self.offset_x = Offset::Relative(offset.x.clamp(0.0, 1.0));
|
||||
fn snap_to(&mut self, offset: RelativeOffset<Option<f32>>) {
|
||||
self.offset_x = Offset::Relative(offset.x.unwrap_or(0.0).clamp(0.0, 1.0));
|
||||
}
|
||||
|
||||
fn scroll_to(&mut self, offset: AbsoluteOffset) {
|
||||
let new_offset = Offset::Absolute(offset.x.max(0.0));
|
||||
fn scroll_to(&mut self, offset: AbsoluteOffset<Option<f32>>) {
|
||||
let new_offset = Offset::Absolute(offset.x.unwrap_or(0.0).max(0.0));
|
||||
self.scroll_animation = Some(ScrollAnimationState {
|
||||
start_time: Instant::now(),
|
||||
start: self.offset_x,
|
||||
|
|
@ -159,7 +161,7 @@ where
|
|||
Element::new(tab.internal(i))
|
||||
});
|
||||
|
||||
let tabs_rule = widget::vertical_rule(4).class(if tabs.len() - 1 == active {
|
||||
let tabs_rule = widget::rule::vertical(4).class(if tabs.len() - 1 == active {
|
||||
if activated {
|
||||
TabRuleTheme::ActiveActivated
|
||||
} else {
|
||||
|
|
@ -193,12 +195,12 @@ where
|
|||
|
||||
let mut elements = Vec::with_capacity(tabs.len() + 5);
|
||||
|
||||
elements.push(widget::vertical_rule(4).class(rule_style).into());
|
||||
elements.push(widget::rule::vertical(4).class(rule_style).into());
|
||||
elements.push(prev_button.into());
|
||||
elements.extend(tabs);
|
||||
elements.push(tabs_rule.into());
|
||||
elements.push(next_button.into());
|
||||
elements.push(widget::vertical_rule(4).class(rule_style).into());
|
||||
elements.push(widget::rule::vertical(4).class(rule_style).into());
|
||||
|
||||
Tabs {
|
||||
elements,
|
||||
|
|
@ -340,9 +342,9 @@ where
|
|||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn layout(&self, tree: &mut Tree, renderer: &cosmic::Renderer, limits: &Limits) -> Node {
|
||||
fn layout(&mut self, tree: &mut Tree, renderer: &cosmic::Renderer, limits: &Limits) -> Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
|
||||
let element_count = self.elements.len();
|
||||
// calculate the smallest possible size
|
||||
let child_limits = Limits::new(
|
||||
Size::new(0.0, limits.min().height),
|
||||
|
|
@ -351,10 +353,13 @@ where
|
|||
.width(Length::Shrink)
|
||||
.height(Length::Shrink);
|
||||
|
||||
let mut nodes = self.elements[2..self.elements.len() - 2]
|
||||
.iter()
|
||||
let mut nodes = self.elements[2..element_count - 2]
|
||||
.iter_mut()
|
||||
.zip(tree.children.iter_mut().skip(2))
|
||||
.map(|(tab, tab_tree)| tab.as_widget().layout(tab_tree, renderer, &child_limits))
|
||||
.map(|(tab, tab_tree)| {
|
||||
tab.as_widget_mut()
|
||||
.layout(tab_tree, renderer, &child_limits)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// sum up
|
||||
|
|
@ -370,8 +375,9 @@ where
|
|||
if min_size.width <= size.width {
|
||||
// we don't need to scroll
|
||||
|
||||
let element_count = self.elements.len();
|
||||
// can we make every tab equal weight and keep the active large enough?
|
||||
let children = if (size.width / (self.elements.len() as f32 - 5.)).ceil() as i32
|
||||
let children = if (size.width / (element_count as f32 - 5.)).ceil() as i32
|
||||
>= MIN_ACTIVE_TAB_WIDTH
|
||||
{
|
||||
// just use a flex layout
|
||||
|
|
@ -384,20 +390,20 @@ where
|
|||
0.into(),
|
||||
0.,
|
||||
cosmic::iced::Alignment::Center,
|
||||
&self.elements[2..self.elements.len() - 2],
|
||||
&mut tree.children[2..self.elements.len() - 2],
|
||||
&mut self.elements[2..element_count - 2],
|
||||
&mut tree.children[2..element_count - 2],
|
||||
)
|
||||
.children()
|
||||
.to_vec()
|
||||
} else {
|
||||
// otherwise we need a more manual approach
|
||||
let min_width = (size.width - MIN_ACTIVE_TAB_WIDTH as f32 - 4.)
|
||||
/ (self.elements.len() as f32 - 6.);
|
||||
let min_width =
|
||||
(size.width - MIN_ACTIVE_TAB_WIDTH as f32 - 4.) / (element_count as f32 - 6.);
|
||||
let mut offset = 0.;
|
||||
|
||||
let mut nodes = self.elements[2..self.elements.len() - 3]
|
||||
.iter()
|
||||
.zip(tree.children[2..].iter_mut())
|
||||
let mut nodes = self.elements[2..element_count - 3]
|
||||
.iter_mut()
|
||||
.zip(tree.children[2..element_count - 3].iter_mut())
|
||||
.map(|(tab, tab_tree)| {
|
||||
let child_limits = Limits::new(
|
||||
Size::new(min_width, limits.min().height),
|
||||
|
|
@ -406,7 +412,9 @@ where
|
|||
.width(Length::Shrink)
|
||||
.height(Length::Shrink);
|
||||
|
||||
let mut node = tab.as_widget().layout(tab_tree, renderer, &child_limits);
|
||||
let mut node =
|
||||
tab.as_widget_mut()
|
||||
.layout(tab_tree, renderer, &child_limits);
|
||||
node = node.move_to(Point::new(offset, 0.));
|
||||
offset += node.bounds().width;
|
||||
node
|
||||
|
|
@ -508,6 +516,7 @@ where
|
|||
let background_style = Catalog::style(
|
||||
theme,
|
||||
&theme::Container::custom(|theme| widget::container::Style {
|
||||
snap: true,
|
||||
icon_color: None,
|
||||
text_color: None,
|
||||
background: Some(Background::Color(
|
||||
|
|
@ -605,6 +614,9 @@ where
|
|||
let cursor = match cursor {
|
||||
mouse::Cursor::Available(point) => mouse::Cursor::Available(point + offset),
|
||||
mouse::Cursor::Unavailable => mouse::Cursor::Unavailable,
|
||||
mouse::Cursor::Levitating(point) => {
|
||||
mouse::Cursor::Levitating(point + offset)
|
||||
}
|
||||
};
|
||||
|
||||
renderer.with_layer(bounds, |renderer| {
|
||||
|
|
@ -677,7 +689,7 @@ where
|
|||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &cosmic::Renderer,
|
||||
|
|
@ -691,38 +703,38 @@ where
|
|||
state.cleanup_old_animations();
|
||||
|
||||
operation.scrollable(
|
||||
state,
|
||||
self.id.as_ref(),
|
||||
bounds,
|
||||
content_bounds,
|
||||
Vector { x: 0.0, y: 0.0 }, /* seemingly unused */
|
||||
Vector { x: 0.0, y: 0.0 },
|
||||
state,
|
||||
);
|
||||
|
||||
operation.container(self.id.as_ref(), bounds, &mut |operation| {
|
||||
self.elements[2..self.elements.len() - 3]
|
||||
.iter()
|
||||
let element_count = self.elements.len();
|
||||
operation.traverse(&mut |operation| {
|
||||
self.elements[2..element_count - 3]
|
||||
.iter_mut()
|
||||
.zip(tree.children.iter_mut().skip(2))
|
||||
.zip(layout.children().skip(2))
|
||||
.for_each(|((child, state), layout)| {
|
||||
child
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.operate(state, layout, renderer, operation);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn on_event(
|
||||
fn update(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: event::Event,
|
||||
event: &event::Event,
|
||||
layout: Layout<'_>,
|
||||
cursor: mouse::Cursor,
|
||||
renderer: &cosmic::Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
viewport: &Rectangle,
|
||||
) -> event::Status {
|
||||
) {
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
state.cleanup_old_animations();
|
||||
|
||||
|
|
@ -849,15 +861,15 @@ where
|
|||
let mut internal_shell = Shell::new(&mut messages);
|
||||
|
||||
let len = self.elements.len();
|
||||
let result = if scrolling && cursor.position().is_some_and(|pos| pos.x < bounds.x) {
|
||||
if scrolling && cursor.position().is_some_and(|pos| pos.x < bounds.x) {
|
||||
self.elements[0..2]
|
||||
.iter_mut()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.map(|((child, state), layout)| {
|
||||
child.as_widget_mut().on_event(
|
||||
child.as_widget_mut().update(
|
||||
state,
|
||||
event.clone(),
|
||||
event,
|
||||
layout,
|
||||
cursor,
|
||||
renderer,
|
||||
|
|
@ -865,8 +877,7 @@ where
|
|||
&mut internal_shell,
|
||||
viewport,
|
||||
)
|
||||
})
|
||||
.fold(event::Status::Ignored, event::Status::merge)
|
||||
});
|
||||
} else if scrolling
|
||||
&& cursor
|
||||
.position()
|
||||
|
|
@ -877,9 +888,9 @@ where
|
|||
.zip(tree.children.iter_mut().skip(len - 3))
|
||||
.zip(layout.children().skip(len - 3))
|
||||
.map(|((child, state), layout)| {
|
||||
child.as_widget_mut().on_event(
|
||||
child.as_widget_mut().update(
|
||||
state,
|
||||
event.clone(),
|
||||
event,
|
||||
layout,
|
||||
cursor,
|
||||
renderer,
|
||||
|
|
@ -887,8 +898,7 @@ where
|
|||
&mut internal_shell,
|
||||
viewport,
|
||||
)
|
||||
})
|
||||
.fold(event::Status::Ignored, event::Status::merge)
|
||||
});
|
||||
} else {
|
||||
self.elements[2..len - 3]
|
||||
.iter_mut()
|
||||
|
|
@ -898,11 +908,14 @@ where
|
|||
let cursor = match cursor {
|
||||
mouse::Cursor::Available(point) => mouse::Cursor::Available(point + offset),
|
||||
mouse::Cursor::Unavailable => mouse::Cursor::Unavailable,
|
||||
mouse::Cursor::Levitating(point) => {
|
||||
mouse::Cursor::Levitating(point + offset)
|
||||
}
|
||||
};
|
||||
|
||||
child.as_widget_mut().on_event(
|
||||
child.as_widget_mut().update(
|
||||
state,
|
||||
event.clone(),
|
||||
event,
|
||||
layout,
|
||||
cursor,
|
||||
renderer,
|
||||
|
|
@ -910,8 +923,7 @@ where
|
|||
&mut internal_shell,
|
||||
viewport,
|
||||
)
|
||||
})
|
||||
.fold(event::Status::Ignored, event::Status::merge)
|
||||
});
|
||||
};
|
||||
|
||||
for mut message in messages {
|
||||
|
|
@ -919,14 +931,12 @@ where
|
|||
x: state.offset_x.absolute(bounds.width, content_bounds.width),
|
||||
y: 0.,
|
||||
}) {
|
||||
state.scroll_to(offset);
|
||||
state.scroll_to(offset.into());
|
||||
continue;
|
||||
}
|
||||
|
||||
shell.publish(message);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
|
|
@ -992,6 +1002,9 @@ where
|
|||
let cursor = match cursor {
|
||||
mouse::Cursor::Available(point) => mouse::Cursor::Available(point + offset),
|
||||
mouse::Cursor::Unavailable => mouse::Cursor::Unavailable,
|
||||
mouse::Cursor::Levitating(point) => {
|
||||
mouse::Cursor::Levitating(point + offset)
|
||||
}
|
||||
};
|
||||
|
||||
child.as_widget().mouse_interaction(
|
||||
|
|
@ -1010,10 +1023,18 @@ where
|
|||
fn overlay<'b>(
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
layout: Layout<'b>,
|
||||
renderer: &cosmic::Renderer,
|
||||
viewport: &Rectangle,
|
||||
translation: cosmic::iced::Vector,
|
||||
) -> Option<overlay::Element<'b, Message, cosmic::Theme, cosmic::Renderer>> {
|
||||
overlay::from_children(&mut self.elements, tree, layout, renderer, translation)
|
||||
overlay::from_children(
|
||||
&mut self.elements,
|
||||
tree,
|
||||
layout,
|
||||
renderer,
|
||||
viewport,
|
||||
translation,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use cosmic::{
|
|||
},
|
||||
iced_core::{Background, Border, Color, Length},
|
||||
theme,
|
||||
widget::{horizontal_space, icon::from_name, text},
|
||||
widget::{icon::from_name, space, text},
|
||||
};
|
||||
use smithay::utils::{Logical, Size};
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ impl Program for StackHoverInternal {
|
|||
.prefer_svg(true)
|
||||
.icon()
|
||||
.into(),
|
||||
horizontal_space().width(16).into(),
|
||||
space::horizontal().width(16).into(),
|
||||
text::title3(fl!("stack-windows")).into(),
|
||||
])
|
||||
.align_y(Alignment::Center)
|
||||
|
|
@ -48,6 +48,7 @@ impl Program for StackHoverInternal {
|
|||
.padding(16)
|
||||
.apply(container)
|
||||
.class(theme::Container::custom(|theme| container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
use calloop::LoopHandle;
|
||||
use cosmic::{
|
||||
Apply,
|
||||
iced::widget::{container, horizontal_space, row},
|
||||
iced::widget::{container, row, space},
|
||||
iced_core::{Alignment, Background, Border, Color, Length},
|
||||
theme,
|
||||
widget::{icon::from_name, text},
|
||||
|
|
@ -34,7 +34,7 @@ impl Program for SwapIndicatorInternal {
|
|||
.prefer_svg(true)
|
||||
.icon()
|
||||
.into(),
|
||||
horizontal_space().width(16).into(),
|
||||
space::horizontal().width(16).into(),
|
||||
text::title3(fl!("swap-windows")).into(),
|
||||
])
|
||||
.align_y(Alignment::Center)
|
||||
|
|
@ -44,6 +44,7 @@ impl Program for SwapIndicatorInternal {
|
|||
.padding(16)
|
||||
.apply(container)
|
||||
.class(theme::Container::custom(|theme| container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||
text_color: Some(Color::from(theme.cosmic().accent.on)),
|
||||
background: Some(Background::Color(theme.cosmic().accent_color().into())),
|
||||
|
|
|
|||
|
|
@ -52,13 +52,13 @@ where
|
|||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
&mut self,
|
||||
state: &mut Tree,
|
||||
renderer: &cosmic::Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let state = &mut state.children[0];
|
||||
let node = self.elem.as_widget().layout(state, renderer, limits);
|
||||
let node = self.elem.as_widget_mut().layout(state, renderer, limits);
|
||||
layout::Node::with_children(node.size(), vec![node])
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +81,7 @@ where
|
|||
|
||||
renderer.fill_quad(
|
||||
Quad {
|
||||
snap: true,
|
||||
bounds: layout.bounds(),
|
||||
border: Border {
|
||||
radius: styling.border_radius,
|
||||
|
|
@ -128,7 +129,7 @@ where
|
|||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
state: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &cosmic::Renderer,
|
||||
|
|
@ -137,21 +138,21 @@ where
|
|||
let state = &mut state.children[0];
|
||||
let layout = layout.children().next().unwrap();
|
||||
self.elem
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.operate(state, layout, renderer, operation)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
fn update(
|
||||
&mut self,
|
||||
state: &mut Tree,
|
||||
event: Event,
|
||||
event: &Event,
|
||||
layout: Layout<'_>,
|
||||
cursor: mouse::Cursor,
|
||||
renderer: &cosmic::Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
viewport: &Rectangle,
|
||||
) -> event::Status {
|
||||
) {
|
||||
let mut bounds = layout.bounds();
|
||||
|
||||
// fix padding 1 and event... don't ask.
|
||||
|
|
@ -180,9 +181,9 @@ where
|
|||
|
||||
let state = &mut state.children[0];
|
||||
let layout = layout.children().next().unwrap();
|
||||
self.elem.as_widget_mut().on_event(
|
||||
self.elem.as_widget_mut().update(
|
||||
state, event, layout, cursor, renderer, clipboard, shell, viewport,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
|
|
@ -203,15 +204,16 @@ where
|
|||
fn overlay<'b>(
|
||||
&'b mut self,
|
||||
state: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
layout: Layout<'b>,
|
||||
renderer: &cosmic::Renderer,
|
||||
viewport: &Rectangle,
|
||||
translation: cosmic::iced::Vector,
|
||||
) -> Option<overlay::Element<'b, Message, cosmic::Theme, cosmic::Renderer>> {
|
||||
let state = &mut state.children[0];
|
||||
let layout = layout.children().next().unwrap();
|
||||
self.elem
|
||||
.as_widget_mut()
|
||||
.overlay(state, layout, renderer, translation)
|
||||
.overlay(state, layout, renderer, viewport, translation)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use cosmic::{
|
|||
iced_core::{Border, Length, Rectangle as IcedRectangle, alignment::Horizontal},
|
||||
iced_widget::{self, Column, Row, text::Style as TextStyle},
|
||||
theme,
|
||||
widget::{button, divider, horizontal_space, icon::from_name, text},
|
||||
widget::{button, divider, icon::from_name, space, text},
|
||||
};
|
||||
use smithay::{
|
||||
backend::{
|
||||
|
|
@ -392,7 +392,7 @@ impl Program for ContextMenu {
|
|||
match item {
|
||||
Item::Separator => divider::horizontal::light().into(),
|
||||
Item::Submenu { title, .. } => Row::with_children(vec![
|
||||
horizontal_space().width(16).into(),
|
||||
space::horizontal().width(16).into(),
|
||||
text::body(title).width(mode).into(),
|
||||
from_name("go-next-symbolic")
|
||||
.size(16)
|
||||
|
|
@ -425,7 +425,7 @@ impl Program for ContextMenu {
|
|||
}))
|
||||
.into()
|
||||
} else {
|
||||
horizontal_space().width(16).into()
|
||||
space::horizontal().width(16).into()
|
||||
},
|
||||
text::body(title)
|
||||
.width(mode)
|
||||
|
|
@ -441,7 +441,7 @@ impl Program for ContextMenu {
|
|||
theme::Text::Default
|
||||
})
|
||||
.into(),
|
||||
horizontal_space().width(16).into(),
|
||||
space::horizontal().width(16).into(),
|
||||
];
|
||||
if let Some(shortcut) = shortcut.as_ref() {
|
||||
components.push(
|
||||
|
|
@ -479,6 +479,7 @@ impl Program for ContextMenu {
|
|||
let cosmic = theme.cosmic();
|
||||
let component = &cosmic.background.component;
|
||||
iced_widget::container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(cosmic.accent.base.into()),
|
||||
text_color: Some(component.on.into()),
|
||||
background: Some(Background::Color(component.base.into())),
|
||||
|
|
|
|||
|
|
@ -514,6 +514,7 @@ impl Program for ZoomProgram {
|
|||
let cosmic = theme.cosmic();
|
||||
let component = &cosmic.background.component;
|
||||
iced_widget::container::Style {
|
||||
snap: true,
|
||||
icon_color: Some(component.on.into()),
|
||||
text_color: Some(component.on.into()),
|
||||
background: Some(Background::Color(component.base.into())),
|
||||
|
|
|
|||
|
|
@ -18,11 +18,7 @@ use cosmic::{
|
|||
window::Event as WindowEvent,
|
||||
},
|
||||
iced_core::{Color, Length, Pixels, clipboard::Null as NullClipboard, id::Id, renderer::Style},
|
||||
iced_runtime::{
|
||||
Action, Debug,
|
||||
program::{Program as IcedProgram, State},
|
||||
task::into_stream,
|
||||
},
|
||||
iced_runtime::{Action, task::into_stream},
|
||||
};
|
||||
use iced_tiny_skia::{
|
||||
Layer,
|
||||
|
|
@ -66,6 +62,8 @@ use smithay::{
|
|||
},
|
||||
};
|
||||
|
||||
use crate::utils::state::State;
|
||||
|
||||
static ID: LazyLock<Id> = LazyLock::new(|| Id::new("Program"));
|
||||
|
||||
pub struct IcedElement<P: Program + Send + 'static>(pub(crate) Arc<Mutex<IcedElementInternal<P>>>);
|
||||
|
|
@ -99,6 +97,26 @@ impl<P: Program + Send + 'static> Hash for IcedElement<P> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait IcedProgram {
|
||||
type Message: std::fmt::Debug + Send;
|
||||
fn update(&mut self, _message: Self::Message) -> Task<Self::Message> {
|
||||
Task::none()
|
||||
}
|
||||
fn view(&self) -> cosmic::Element<'_, Self::Message>;
|
||||
|
||||
fn background_color(&self) -> Color {
|
||||
Color::TRANSPARENT
|
||||
}
|
||||
|
||||
fn foreground(
|
||||
&self,
|
||||
_pixels: &mut tiny_skia::PixmapMut<'_>,
|
||||
_damage: &[Rectangle<i32, BufferCoords>],
|
||||
_scale: f32,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Program {
|
||||
type Message: std::fmt::Debug + Send;
|
||||
fn update(
|
||||
|
|
@ -135,8 +153,6 @@ struct ProgramWrapper<P: Program> {
|
|||
|
||||
impl<P: Program> IcedProgram for ProgramWrapper<P> {
|
||||
type Message = <P as Program>::Message;
|
||||
type Renderer = cosmic::Renderer;
|
||||
type Theme = cosmic::Theme;
|
||||
|
||||
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
|
||||
let last_seat = self.last_seat.lock().unwrap();
|
||||
|
|
@ -165,7 +181,6 @@ pub(crate) struct IcedElementInternal<P: Program + Send + 'static> {
|
|||
theme: Theme,
|
||||
renderer: cosmic::Renderer,
|
||||
state: State<ProgramWrapper<P>>,
|
||||
debug: Debug,
|
||||
|
||||
// futures
|
||||
handle: LoopHandle<'static, crate::state::State>,
|
||||
|
|
@ -189,7 +204,6 @@ impl<P: Program + Send + Clone + 'static> Clone for IcedElementInternal<P> {
|
|||
tracing::warn!("Missing force_update call");
|
||||
}
|
||||
let mut renderer = cosmic::Renderer::new(cosmic::font::default(), Pixels(16.0));
|
||||
let mut debug = Debug::new();
|
||||
let state = State::new(
|
||||
ID.clone(),
|
||||
ProgramWrapper {
|
||||
|
|
@ -199,7 +213,6 @@ impl<P: Program + Send + Clone + 'static> Clone for IcedElementInternal<P> {
|
|||
},
|
||||
IcedSize::new(self.size.w as f32, self.size.h as f32),
|
||||
&mut renderer,
|
||||
&mut debug,
|
||||
);
|
||||
|
||||
IcedElementInternal {
|
||||
|
|
@ -214,7 +227,6 @@ impl<P: Program + Send + Clone + 'static> Clone for IcedElementInternal<P> {
|
|||
theme: self.theme.clone(),
|
||||
renderer,
|
||||
state,
|
||||
debug,
|
||||
handle,
|
||||
scheduler,
|
||||
executor_token,
|
||||
|
|
@ -240,7 +252,6 @@ impl<P: Program + Send + 'static> fmt::Debug for IcedElementInternal<P> {
|
|||
.field("theme", &"...")
|
||||
.field("renderer", &"...")
|
||||
.field("state", &"...")
|
||||
.field("debug", &self.debug)
|
||||
.field("handle", &self.handle)
|
||||
.field("scheduler", &self.scheduler)
|
||||
.field("executor_token", &self.executor_token)
|
||||
|
|
@ -265,7 +276,6 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
|||
let size = size.into();
|
||||
let last_seat = Arc::new(Mutex::new(None));
|
||||
let mut renderer = cosmic::Renderer::new(cosmic::font::default(), Pixels(16.0));
|
||||
let mut debug = Debug::new();
|
||||
|
||||
let state = State::new(
|
||||
ID.clone(),
|
||||
|
|
@ -276,7 +286,6 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
|||
},
|
||||
IcedSize::new(size.w as f32, size.h as f32),
|
||||
&mut renderer,
|
||||
&mut debug,
|
||||
);
|
||||
|
||||
let (executor, scheduler) = calloop::futures::executor().expect("Out of file descriptors");
|
||||
|
|
@ -299,7 +308,6 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
|||
theme,
|
||||
renderer,
|
||||
state,
|
||||
debug,
|
||||
handle,
|
||||
scheduler,
|
||||
executor_token,
|
||||
|
|
@ -317,14 +325,16 @@ impl<P: Program + Send + 'static> IcedElement<P> {
|
|||
|
||||
pub fn minimum_size(&self) -> Size<i32, Logical> {
|
||||
let internal = self.0.lock().unwrap();
|
||||
let element = internal.state.program().program.view();
|
||||
let mut element = internal.state.program().program.view();
|
||||
|
||||
let mut tree = Tree::new(element.as_widget());
|
||||
let node = element
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.layout(
|
||||
// TODO Avoid creating a new tree here?
|
||||
&mut Tree::new(element.as_widget()),
|
||||
&mut tree,
|
||||
&internal.renderer,
|
||||
&Limits::new(IcedSize::ZERO, IcedSize::INFINITY)
|
||||
&Limits::new(IcedSize::ZERO, IcedSize::INFINITE)
|
||||
.width(Length::Shrink)
|
||||
.height(Length::Shrink),
|
||||
)
|
||||
|
|
@ -432,7 +442,6 @@ impl<P: Program + Send + 'static> IcedElementInternal<P> {
|
|||
text_color: self.theme.cosmic().on_bg_color().into(),
|
||||
},
|
||||
&mut NullClipboard,
|
||||
&mut self.debug,
|
||||
)
|
||||
.1;
|
||||
|
||||
|
|
@ -922,7 +931,6 @@ where
|
|||
if size.w > 0 && size.h > 0 {
|
||||
let state_ref = &internal_ref.state;
|
||||
let mut clip_mask = tiny_skia::Mask::new(size.w as u32, size.h as u32).unwrap();
|
||||
let overlay = internal_ref.debug.overlay();
|
||||
let theme = &internal_ref.theme;
|
||||
|
||||
_ = buffer.render().draw(|buf| {
|
||||
|
|
@ -977,7 +985,6 @@ where
|
|||
&viewport,
|
||||
&damage,
|
||||
background_color,
|
||||
&overlay,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,4 +9,5 @@ pub mod prelude;
|
|||
pub mod quirks;
|
||||
pub mod rlimit;
|
||||
pub mod screenshot;
|
||||
pub mod state;
|
||||
pub mod tween;
|
||||
|
|
|
|||
222
src/utils/state.rs
Normal file
222
src/utils/state.rs
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
use super::iced::IcedProgram as Program;
|
||||
use cosmic::iced::core::event::{self, Event};
|
||||
use cosmic::iced::core::mouse;
|
||||
use cosmic::iced::core::renderer;
|
||||
use cosmic::iced::core::widget::operation::{self, Operation};
|
||||
use cosmic::iced::core::{Clipboard, Size};
|
||||
use cosmic::iced_core;
|
||||
use cosmic::iced_runtime::Task;
|
||||
use cosmic::iced_runtime::user_interface::{self, UserInterface};
|
||||
|
||||
/// The execution state of a [`Program`]. It leverages caching, event
|
||||
/// processing, and rendering primitive storage.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct State<P>
|
||||
where
|
||||
P: Program + 'static,
|
||||
{
|
||||
program: P,
|
||||
cache: Option<user_interface::Cache>,
|
||||
queued_events: Vec<Event>,
|
||||
queued_messages: Vec<P::Message>,
|
||||
mouse_interaction: mouse::Interaction,
|
||||
}
|
||||
|
||||
impl<P> State<P>
|
||||
where
|
||||
P: Program + 'static,
|
||||
{
|
||||
/// Creates a new [`State`] with the provided [`Program`], initializing its
|
||||
/// primitive with the given logical bounds and renderer.
|
||||
pub fn new(
|
||||
id: iced_core::id::Id,
|
||||
mut program: P,
|
||||
bounds: Size,
|
||||
renderer: &mut cosmic::Renderer,
|
||||
) -> Self {
|
||||
let user_interface = build_user_interface(
|
||||
id,
|
||||
&mut program,
|
||||
user_interface::Cache::default(),
|
||||
renderer,
|
||||
bounds,
|
||||
);
|
||||
|
||||
let cache = Some(user_interface.into_cache());
|
||||
|
||||
State {
|
||||
program,
|
||||
cache,
|
||||
queued_events: Vec::new(),
|
||||
queued_messages: Vec::new(),
|
||||
mouse_interaction: mouse::Interaction::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the [`Program`] of the [`State`].
|
||||
pub fn program(&self) -> &P {
|
||||
&self.program
|
||||
}
|
||||
|
||||
/// Queues an event in the [`State`] for processing during an [`update`].
|
||||
///
|
||||
/// [`update`]: Self::update
|
||||
pub fn queue_event(&mut self, event: Event) {
|
||||
self.queued_events.push(event);
|
||||
}
|
||||
|
||||
/// Queues a message in the [`State`] for processing during an [`update`].
|
||||
///
|
||||
/// [`update`]: Self::update
|
||||
pub fn queue_message(&mut self, message: P::Message) {
|
||||
self.queued_messages.push(message);
|
||||
}
|
||||
|
||||
/// Returns whether the event queue of the [`State`] is empty or not.
|
||||
pub fn is_queue_empty(&self) -> bool {
|
||||
self.queued_events.is_empty() && self.queued_messages.is_empty()
|
||||
}
|
||||
|
||||
/// Returns the current [`mouse::Interaction`] of the [`State`].
|
||||
pub fn mouse_interaction(&self) -> mouse::Interaction {
|
||||
self.mouse_interaction
|
||||
}
|
||||
|
||||
/// Processes all the queued events and messages, rebuilding and redrawing
|
||||
/// the widgets of the linked [`Program`] if necessary.
|
||||
///
|
||||
/// Returns a list containing the instances of [`Event`] that were not
|
||||
/// captured by any widget, and the [`Task`] obtained from [`Program`]
|
||||
/// after updating it, only if an update was necessary.
|
||||
pub fn update(
|
||||
&mut self,
|
||||
id: iced_core::id::Id,
|
||||
bounds: Size,
|
||||
cursor: mouse::Cursor,
|
||||
renderer: &mut cosmic::Renderer,
|
||||
theme: &cosmic::Theme,
|
||||
style: &renderer::Style,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
) -> (Vec<Event>, Option<Task<P::Message>>) {
|
||||
let mut user_interface = build_user_interface(
|
||||
id.clone(),
|
||||
&mut self.program,
|
||||
self.cache.take().unwrap(),
|
||||
renderer,
|
||||
bounds,
|
||||
);
|
||||
|
||||
let mut messages = Vec::new();
|
||||
|
||||
let (state, event_statuses) = user_interface.update(
|
||||
&self.queued_events,
|
||||
cursor,
|
||||
renderer,
|
||||
clipboard,
|
||||
&mut messages,
|
||||
);
|
||||
|
||||
let uncaptured_events = self
|
||||
.queued_events
|
||||
.iter()
|
||||
.zip(event_statuses)
|
||||
.filter_map(|(event, status)| matches!(status, event::Status::Ignored).then_some(event))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
self.queued_events.clear();
|
||||
messages.append(&mut self.queued_messages);
|
||||
|
||||
let task = if messages.is_empty() {
|
||||
if let cosmic::iced_runtime::user_interface::State::Updated {
|
||||
mouse_interaction, ..
|
||||
} = state
|
||||
{
|
||||
self.mouse_interaction = mouse_interaction;
|
||||
}
|
||||
|
||||
user_interface.draw(renderer, theme, style, cursor);
|
||||
|
||||
self.cache = Some(user_interface.into_cache());
|
||||
|
||||
None
|
||||
} else {
|
||||
// When there are messages, we are forced to rebuild twice
|
||||
// for now :^)
|
||||
let temp_cache = user_interface.into_cache();
|
||||
|
||||
let tasks = Task::batch(messages.into_iter().map(|message| {
|
||||
let task = self.program.update(message);
|
||||
|
||||
task
|
||||
}));
|
||||
|
||||
let mut user_interface =
|
||||
build_user_interface(id, &mut self.program, temp_cache, renderer, bounds);
|
||||
|
||||
if let cosmic::iced_runtime::user_interface::State::Updated {
|
||||
mouse_interaction, ..
|
||||
} = state
|
||||
{
|
||||
self.mouse_interaction = mouse_interaction;
|
||||
}
|
||||
|
||||
user_interface.draw(renderer, theme, style, cursor);
|
||||
|
||||
self.cache = Some(user_interface.into_cache());
|
||||
|
||||
Some(tasks)
|
||||
};
|
||||
|
||||
(uncaptured_events, task)
|
||||
}
|
||||
|
||||
/// Applies [`Operation`]s to the [`State`]
|
||||
pub fn operate(
|
||||
&mut self,
|
||||
id: iced_core::id::Id,
|
||||
renderer: &mut cosmic::Renderer,
|
||||
operations: impl Iterator<Item = Box<dyn Operation>>,
|
||||
bounds: Size,
|
||||
) {
|
||||
let mut user_interface = build_user_interface(
|
||||
id,
|
||||
&mut self.program,
|
||||
self.cache.take().unwrap(),
|
||||
renderer,
|
||||
bounds,
|
||||
);
|
||||
|
||||
for operation in operations {
|
||||
let mut current_operation = Some(operation);
|
||||
|
||||
while let Some(mut operation) = current_operation.take() {
|
||||
user_interface.operate(renderer, operation.as_mut());
|
||||
|
||||
match operation.finish() {
|
||||
operation::Outcome::None => {}
|
||||
operation::Outcome::Some(()) => {}
|
||||
operation::Outcome::Chain(next) => {
|
||||
current_operation = Some(next);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
self.cache = Some(user_interface.into_cache());
|
||||
}
|
||||
}
|
||||
|
||||
fn build_user_interface<'a, P: Program>(
|
||||
_id: iced_core::id::Id,
|
||||
program: &'a mut P,
|
||||
cache: user_interface::Cache,
|
||||
renderer: &mut cosmic::Renderer,
|
||||
size: Size,
|
||||
) -> UserInterface<'a, P::Message, cosmic::Theme, cosmic::Renderer> {
|
||||
let view = program.view();
|
||||
|
||||
let user_interface = UserInterface::build(view, size, cache, renderer);
|
||||
|
||||
user_interface
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue