feat: rectangle tracker container
This commit is contained in:
parent
db8b53b836
commit
8b1014a754
5 changed files with 363 additions and 341 deletions
|
|
@ -5,12 +5,14 @@ use cosmic::{
|
||||||
iced::widget::{
|
iced::widget::{
|
||||||
column, container, horizontal_space, pick_list, progress_bar, radio, row, slider,
|
column, container, horizontal_space, pick_list, progress_bar, radio, row, slider,
|
||||||
},
|
},
|
||||||
iced::{self, Alignment, Application, Command, Length, wayland::SurfaceIdWrapper},
|
iced::{self, wayland::SurfaceIdWrapper, Alignment, Application, Command, Length},
|
||||||
iced_lazy::responsive,
|
iced_lazy::responsive,
|
||||||
theme::{self, Theme},
|
theme::{self, Theme},
|
||||||
widget::{button, nav_button, nav_bar, nav_bar_page, nav_bar_section, header_bar, settings, scrollable, toggler},
|
widget::{
|
||||||
Element,
|
button, header_bar, nav_bar, nav_bar_page, nav_bar_section, nav_button, scrollable,
|
||||||
ElementExt,
|
settings, toggler, rectangle_tracker::{RectangleTracker, rectangle_tracker_subscription, RectangleUpdate},
|
||||||
|
},
|
||||||
|
Element, ElementExt
|
||||||
};
|
};
|
||||||
use std::{collections::BTreeMap, vec};
|
use std::{collections::BTreeMap, vec};
|
||||||
use theme::Button as ButtonTheme;
|
use theme::Button as ButtonTheme;
|
||||||
|
|
@ -29,6 +31,7 @@ pub struct Window {
|
||||||
show_minimize: bool,
|
show_minimize: bool,
|
||||||
show_maximize: bool,
|
show_maximize: bool,
|
||||||
exit: bool,
|
exit: bool,
|
||||||
|
rectangle_tracker: Option<RectangleTracker<u32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
|
@ -49,7 +52,7 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
Page(u8),
|
Page(u8),
|
||||||
Debug(bool),
|
Debug(bool),
|
||||||
|
|
@ -66,6 +69,7 @@ pub enum Message {
|
||||||
Minimize,
|
Minimize,
|
||||||
Maximize,
|
Maximize,
|
||||||
InputChanged,
|
InputChanged,
|
||||||
|
Rectangle(RectangleUpdate<u32>)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Window {
|
impl Application for Window {
|
||||||
|
|
@ -99,7 +103,7 @@ impl Application for Window {
|
||||||
Message::SliderChanged(value) => self.slider_value = value,
|
Message::SliderChanged(value) => self.slider_value = value,
|
||||||
Message::CheckboxToggled(value) => {
|
Message::CheckboxToggled(value) => {
|
||||||
self.checkbox_value = value;
|
self.checkbox_value = value;
|
||||||
},
|
}
|
||||||
Message::TogglerToggled(value) => self.toggler_value = value,
|
Message::TogglerToggled(value) => self.toggler_value = value,
|
||||||
Message::PickListSelected(value) => self.pick_list_selected = Some(value),
|
Message::PickListSelected(value) => self.pick_list_selected = Some(value),
|
||||||
Message::Close => self.exit = true,
|
Message::Close => self.exit = true,
|
||||||
|
|
@ -108,8 +112,11 @@ impl Application for Window {
|
||||||
Message::Minimize => todo!(),
|
Message::Minimize => todo!(),
|
||||||
Message::Maximize => todo!(),
|
Message::Maximize => todo!(),
|
||||||
Message::RowSelected(row) => println!("Selected row {row}"),
|
Message::RowSelected(row) => println!("Selected row {row}"),
|
||||||
Message::InputChanged => {},
|
Message::InputChanged => {}
|
||||||
|
Message::Rectangle(r) => match r {
|
||||||
|
RectangleUpdate::Rectangle(r) => {dbg!(r);},
|
||||||
|
RectangleUpdate::Init(t) => {self.rectangle_tracker.replace(t);},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::none()
|
Command::none()
|
||||||
|
|
@ -124,7 +131,7 @@ impl Application for Window {
|
||||||
nav_button("Settings")
|
nav_button("Settings")
|
||||||
.on_sidebar_toggled(Message::ToggleSidebar)
|
.on_sidebar_toggled(Message::ToggleSidebar)
|
||||||
.sidebar_active(self.sidebar_toggled)
|
.sidebar_active(self.sidebar_toggled)
|
||||||
.into()
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.show_maximize {
|
if self.show_maximize {
|
||||||
|
|
@ -226,13 +233,21 @@ impl Application for Window {
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
let secondary = button(ButtonTheme::Secondary)
|
||||||
|
.text("Secondary")
|
||||||
|
.on_press(Message::ButtonPressed);
|
||||||
|
|
||||||
|
let secondary = if let Some(tracker) = self.rectangle_tracker.as_ref() {
|
||||||
|
tracker.container(0, secondary).into()
|
||||||
|
} else {
|
||||||
|
secondary.into()
|
||||||
|
};
|
||||||
let content: Element<_> = settings::view_column(vec![
|
let content: Element<_> = settings::view_column(vec![
|
||||||
settings::view_section("Debug")
|
settings::view_section("Debug")
|
||||||
.add(settings::item("Debug theme", choose_theme))
|
.add(settings::item("Debug theme", choose_theme))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Debug layout",
|
"Debug layout",
|
||||||
toggler(String::from("Debug layout"), self.debug, Message::Debug)
|
toggler(String::from("Debug layout"), self.debug, Message::Debug),
|
||||||
))
|
))
|
||||||
.into(),
|
.into(),
|
||||||
settings::view_section("Buttons")
|
settings::view_section("Buttons")
|
||||||
|
|
@ -241,10 +256,7 @@ impl Application for Window {
|
||||||
.text("Primary")
|
.text("Primary")
|
||||||
.on_press(Message::ButtonPressed)
|
.on_press(Message::ButtonPressed)
|
||||||
.into(),
|
.into(),
|
||||||
button(ButtonTheme::Secondary)
|
secondary,
|
||||||
.text("Secondary")
|
|
||||||
.on_press(Message::ButtonPressed)
|
|
||||||
.into(),
|
|
||||||
button(ButtonTheme::Positive)
|
button(ButtonTheme::Positive)
|
||||||
.text("Positive")
|
.text("Positive")
|
||||||
.on_press(Message::ButtonPressed)
|
.on_press(Message::ButtonPressed)
|
||||||
|
|
@ -256,7 +268,7 @@ impl Application for Window {
|
||||||
button(ButtonTheme::Text)
|
button(ButtonTheme::Text)
|
||||||
.text("Text")
|
.text("Text")
|
||||||
.on_press(Message::ButtonPressed)
|
.on_press(Message::ButtonPressed)
|
||||||
.into()
|
.into(),
|
||||||
]))
|
]))
|
||||||
.add(settings::item_row(vec![
|
.add(settings::item_row(vec![
|
||||||
button(ButtonTheme::Primary).text("Primary").into(),
|
button(ButtonTheme::Primary).text("Primary").into(),
|
||||||
|
|
@ -267,28 +279,31 @@ impl Application for Window {
|
||||||
]))
|
]))
|
||||||
.into(),
|
.into(),
|
||||||
settings::view_section("Controls")
|
settings::view_section("Controls")
|
||||||
.add(settings::item("Toggler", toggler(None, self.toggler_value, Message::TogglerToggled)))
|
.add(settings::item(
|
||||||
|
"Toggler",
|
||||||
|
toggler(None, self.toggler_value, Message::TogglerToggled),
|
||||||
|
))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Pick List (TODO)",
|
"Pick List (TODO)",
|
||||||
pick_list(
|
pick_list(
|
||||||
vec!["Option 1", "Option 2", "Option 3", "Option 4",],
|
vec!["Option 1", "Option 2", "Option 3", "Option 4"],
|
||||||
self.pick_list_selected,
|
self.pick_list_selected,
|
||||||
Message::PickListSelected
|
Message::PickListSelected,
|
||||||
)
|
)
|
||||||
.padding([8, 0, 8, 16])
|
.padding([8, 0, 8, 16]),
|
||||||
))
|
))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Slider",
|
"Slider",
|
||||||
slider(0.0..=100.0, self.slider_value, Message::SliderChanged)
|
slider(0.0..=100.0, self.slider_value, Message::SliderChanged)
|
||||||
.width(Length::Units(250))
|
.width(Length::Units(250)),
|
||||||
))
|
))
|
||||||
.add(settings::item(
|
.add(settings::item(
|
||||||
"Progress",
|
"Progress",
|
||||||
progress_bar(0.0..=100.0, self.slider_value)
|
progress_bar(0.0..=100.0, self.slider_value)
|
||||||
.width(Length::Units(250))
|
.width(Length::Units(250))
|
||||||
.height(Length::Units(4))
|
.height(Length::Units(4)),
|
||||||
))
|
))
|
||||||
.into()
|
.into(),
|
||||||
])
|
])
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
|
@ -327,4 +342,7 @@ impl Application for Window {
|
||||||
fn close_requested(&self, id: SurfaceIdWrapper) -> Self::Message {
|
fn close_requested(&self, id: SurfaceIdWrapper) -> Self::Message {
|
||||||
Message::Close
|
Message::Close
|
||||||
}
|
}
|
||||||
|
fn subscription(&self) -> iced::Subscription<Self::Message> {
|
||||||
|
rectangle_tracker_subscription(0).map(|(i, e)| Message::Rectangle(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,6 @@ pub use self::nav_button::{NavButton, nav_button};
|
||||||
pub mod navigation;
|
pub mod navigation;
|
||||||
pub use navigation::*;
|
pub use navigation::*;
|
||||||
|
|
||||||
pub mod popup;
|
|
||||||
pub use popup::*;
|
|
||||||
|
|
||||||
mod toggler;
|
mod toggler;
|
||||||
pub use toggler::toggler;
|
pub use toggler::toggler;
|
||||||
|
|
||||||
|
|
@ -35,3 +32,5 @@ pub use separator::{horizontal_rule, vertical_rule};
|
||||||
|
|
||||||
pub mod spin_button;
|
pub mod spin_button;
|
||||||
pub use spin_button::{SpinButton, spin_button};
|
pub use spin_button::{SpinButton, spin_button};
|
||||||
|
|
||||||
|
pub mod rectangle_tracker;
|
||||||
|
|
|
||||||
|
|
@ -1,314 +0,0 @@
|
||||||
// Copyright 2022 System76 <info@system76.com>
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
use iced::futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
|
|
||||||
use iced::futures::SinkExt;
|
|
||||||
use iced::{
|
|
||||||
futures::StreamExt,
|
|
||||||
widget::{container, Container},
|
|
||||||
Rectangle,
|
|
||||||
};
|
|
||||||
use iced_native::alignment::{self, Alignment};
|
|
||||||
use iced_native::command::platform_specific::wayland::popup::{SctkPopupSettings, SctkPositioner};
|
|
||||||
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::{
|
|
||||||
window, Background, Clipboard, Color, Element, Layout, Length, Padding, Point, Shell, Widget,
|
|
||||||
};
|
|
||||||
use std::u32;
|
|
||||||
|
|
||||||
pub use iced_style::container::{Appearance, StyleSheet};
|
|
||||||
pub struct SizeTrackingContainer<'a, Message, Renderer>
|
|
||||||
where
|
|
||||||
Renderer: iced_native::Renderer,
|
|
||||||
Renderer::Theme: StyleSheet,
|
|
||||||
{
|
|
||||||
container: Container<'a, Message, Renderer>,
|
|
||||||
tx: UnboundedSender<Rectangle<i32>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message, Renderer> SizeTrackingContainer<'a, Message, Renderer>
|
|
||||||
where
|
|
||||||
Renderer: iced_native::Renderer,
|
|
||||||
Renderer::Theme: StyleSheet,
|
|
||||||
{
|
|
||||||
/// Creates an empty [`Container`].
|
|
||||||
pub fn new<T>(content: T, tx: UnboundedSender<Rectangle<i32>>) -> Self
|
|
||||||
where
|
|
||||||
T: Into<Element<'a, Message, Renderer>>,
|
|
||||||
{
|
|
||||||
SizeTrackingContainer {
|
|
||||||
container: container(content),
|
|
||||||
tx,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the [`Padding`] of the [`Container`].
|
|
||||||
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
|
||||||
self.container = self.container.padding(padding);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the width of the [`Container`].
|
|
||||||
pub fn width(mut self, width: Length) -> Self {
|
|
||||||
self.container = self.container.width(width);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the height of the [`Container`].
|
|
||||||
pub fn height(mut self, height: Length) -> Self {
|
|
||||||
self.container = self.container.height(height);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the maximum width of the [`Container`].
|
|
||||||
pub fn max_width(mut self, max_width: u32) -> Self {
|
|
||||||
self.container = self.container.max_width(max_width);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the maximum height of the [`Container`] in pixels.
|
|
||||||
pub fn max_height(mut self, max_height: u32) -> Self {
|
|
||||||
self.container = self.container.max_height(max_height);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the content alignment for the horizontal axis of the [`Container`].
|
|
||||||
pub fn align_x(mut self, alignment: alignment::Horizontal) -> Self {
|
|
||||||
self.container = self.container.align_x(alignment);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the content alignment for the vertical axis of the [`Container`].
|
|
||||||
pub fn align_y(mut self, alignment: alignment::Vertical) -> Self {
|
|
||||||
self.container = self.container.align_y(alignment);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Centers the contents in the horizontal axis of the [`Container`].
|
|
||||||
pub fn center_x(mut self) -> Self {
|
|
||||||
self.container = self.container.center_x();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Centers the contents in the vertical axis of the [`Container`].
|
|
||||||
pub fn center_y(mut self) -> Self {
|
|
||||||
self.container = self.container.center_y();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the style of the [`Container`].
|
|
||||||
pub fn style(mut self, style: impl Into<<Renderer::Theme as StyleSheet>::Style>) -> Self {
|
|
||||||
self.container = self.container.style(style);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
|
||||||
for SizeTrackingContainer<'a, Message, Renderer>
|
|
||||||
where
|
|
||||||
Renderer: iced_native::Renderer,
|
|
||||||
Renderer::Theme: container::StyleSheet,
|
|
||||||
{
|
|
||||||
fn children(&self) -> Vec<Tree> {
|
|
||||||
self.container.children()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn diff(&self, tree: &mut Tree) {
|
|
||||||
self.container.diff(tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn width(&self) -> Length {
|
|
||||||
Widget::width(&self.container)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn height(&self) -> Length {
|
|
||||||
Widget::height(&self.container)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node {
|
|
||||||
self.container.layout(renderer, limits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_event(
|
|
||||||
&mut self,
|
|
||||||
tree: &mut Tree,
|
|
||||||
event: Event,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor_position: Point,
|
|
||||||
renderer: &Renderer,
|
|
||||||
clipboard: &mut dyn Clipboard,
|
|
||||||
shell: &mut Shell<'_, Message>,
|
|
||||||
) -> event::Status {
|
|
||||||
self.container.on_event(
|
|
||||||
&mut tree.children[0],
|
|
||||||
event,
|
|
||||||
layout,
|
|
||||||
cursor_position,
|
|
||||||
renderer,
|
|
||||||
clipboard,
|
|
||||||
shell,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_interaction(
|
|
||||||
&self,
|
|
||||||
tree: &Tree,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor_position: Point,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
renderer: &Renderer,
|
|
||||||
) -> mouse::Interaction {
|
|
||||||
self.container.mouse_interaction(
|
|
||||||
&tree.children[0],
|
|
||||||
layout,
|
|
||||||
cursor_position,
|
|
||||||
viewport,
|
|
||||||
renderer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
tree: &Tree,
|
|
||||||
renderer: &mut Renderer,
|
|
||||||
theme: &Renderer::Theme,
|
|
||||||
inherited_style: &renderer::Style,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor_position: Point,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
) {
|
|
||||||
let Rectangle {
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
} = layout.bounds();
|
|
||||||
let _ = self.tx.unbounded_send(Rectangle {
|
|
||||||
x: x as i32,
|
|
||||||
y: y as i32,
|
|
||||||
width: width as i32,
|
|
||||||
height: height as i32,
|
|
||||||
});
|
|
||||||
self.container.draw(
|
|
||||||
&tree.children[0],
|
|
||||||
renderer,
|
|
||||||
theme,
|
|
||||||
inherited_style,
|
|
||||||
layout,
|
|
||||||
cursor_position,
|
|
||||||
viewport,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overlay<'b>(
|
|
||||||
&'b self,
|
|
||||||
tree: &'b mut Tree,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
renderer: &Renderer,
|
|
||||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
|
||||||
self.container
|
|
||||||
.overlay(&mut tree.children[0], layout, renderer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PopupParentSubscription {
|
|
||||||
id: window::Id,
|
|
||||||
settings: SctkPopupSettings,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PopupParentSubscription {
|
|
||||||
pub fn new(id: window::Id, settings: SctkPopupSettings) -> Self {
|
|
||||||
Self { id, settings }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_popup_container<'a, T, Message, Renderer>(
|
|
||||||
&self,
|
|
||||||
content: T,
|
|
||||||
tx: UnboundedSender<Rectangle<i32>>,
|
|
||||||
) -> SizeTrackingContainer<'a, Message, Renderer>
|
|
||||||
where
|
|
||||||
T: Into<Element<'a, Message, Renderer>>,
|
|
||||||
Renderer: iced_native::Renderer,
|
|
||||||
Renderer::Theme: StyleSheet,
|
|
||||||
{
|
|
||||||
SizeTrackingContainer::new(content, tx.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subscription(&self) -> iced::Subscription<(window::Id, PositionerUpdate)> {
|
|
||||||
popup_resize(self.id, self.settings.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn popup_resize(
|
|
||||||
id: window::Id,
|
|
||||||
settings: SctkPopupSettings,
|
|
||||||
) -> iced::Subscription<(window::Id, PositionerUpdate)> {
|
|
||||||
iced_native::subscription::unfold(
|
|
||||||
id,
|
|
||||||
State::Init(settings.positioner.anchor_rect.clone()),
|
|
||||||
move |state| rectangle_size(id, state),
|
|
||||||
)
|
|
||||||
.with(settings)
|
|
||||||
.map(|(settings, (id, update))| match update {
|
|
||||||
RectangleUpdate::Update(rect) => {
|
|
||||||
let mut new_pos = settings.positioner.clone();
|
|
||||||
new_pos.anchor_rect = rect;
|
|
||||||
(id, PositionerUpdate::Update(new_pos))
|
|
||||||
}
|
|
||||||
RectangleUpdate::Finished => (id, PositionerUpdate::Finished),
|
|
||||||
RectangleUpdate::Sender(sender) => (id, PositionerUpdate::Sender(sender)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum PositionerUpdate {
|
|
||||||
Sender(UnboundedSender<Rectangle<i32>>),
|
|
||||||
Update(SctkPositioner),
|
|
||||||
Finished,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum RectangleUpdate {
|
|
||||||
Sender(UnboundedSender<Rectangle<i32>>),
|
|
||||||
Update(Rectangle<i32>),
|
|
||||||
Finished,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum State {
|
|
||||||
Init(Rectangle<i32>),
|
|
||||||
WaitForUpdate(Rectangle<i32>, UnboundedReceiver<Rectangle<i32>>),
|
|
||||||
Finished,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn rectangle_size<I: Copy>(id: I, state: State) -> (Option<(I, RectangleUpdate)>, State) {
|
|
||||||
match state {
|
|
||||||
State::Init(rectangle) => {
|
|
||||||
let (tx, rx) = unbounded();
|
|
||||||
(
|
|
||||||
Some((id, RectangleUpdate::Sender(tx))),
|
|
||||||
State::WaitForUpdate(rectangle, rx),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
State::WaitForUpdate(old_rectangle, mut rx) => {
|
|
||||||
let response = rx.next().await;
|
|
||||||
|
|
||||||
match response {
|
|
||||||
Some(new_rectangle) => {
|
|
||||||
let new_update = if new_rectangle == old_rectangle {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((id, RectangleUpdate::Update(new_rectangle)))
|
|
||||||
};
|
|
||||||
(new_update, State::WaitForUpdate(new_rectangle, rx))
|
|
||||||
}
|
|
||||||
None => (Some((id, RectangleUpdate::Finished)), State::Finished),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
State::Finished => iced::futures::future::pending().await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
244
src/widget/rectangle_tracker/mod.rs
Normal file
244
src/widget/rectangle_tracker/mod.rs
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
mod subscription;
|
||||||
|
|
||||||
|
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 std::{fmt::Debug, hash::Hash};
|
||||||
|
|
||||||
|
pub use iced_style::container::{Appearance, StyleSheet};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RectangleTracker<I> {
|
||||||
|
tx: UnboundedSender<(I, Rectangle)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> RectangleTracker<I>
|
||||||
|
where
|
||||||
|
I: Hash + Copy + Send + Sync + Debug,
|
||||||
|
{
|
||||||
|
pub fn container<'a, Message: 'static, T>(
|
||||||
|
&self,
|
||||||
|
id: I,
|
||||||
|
content: T,
|
||||||
|
) -> RectangleTrackingContainer<'a, Message, crate::Renderer, I>
|
||||||
|
where
|
||||||
|
I: 'a,
|
||||||
|
T: Into<Element<'a, Message, crate::Renderer>>,
|
||||||
|
{
|
||||||
|
RectangleTrackingContainer::new(content, id, self.tx.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An element decorating some content.
|
||||||
|
///
|
||||||
|
/// It is normally used for alignment purposes.
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct RectangleTrackingContainer<'a, Message, Renderer, I>
|
||||||
|
where
|
||||||
|
Renderer: iced_native::Renderer,
|
||||||
|
Renderer::Theme: StyleSheet,
|
||||||
|
{
|
||||||
|
tx: UnboundedSender<(I, Rectangle)>,
|
||||||
|
id: I,
|
||||||
|
container: Container<'a, Message, Renderer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Renderer, I> RectangleTrackingContainer<'a, Message, Renderer, I>
|
||||||
|
where
|
||||||
|
Renderer: iced_native::Renderer,
|
||||||
|
Renderer::Theme: StyleSheet,
|
||||||
|
I: 'a + Hash + Copy + Send + Sync + Debug,
|
||||||
|
{
|
||||||
|
/// Creates an empty [`Container`].
|
||||||
|
pub(crate) fn new<T>(content: T, id: I, tx: UnboundedSender<(I, Rectangle)>) -> Self
|
||||||
|
where
|
||||||
|
T: Into<Element<'a, Message, Renderer>>,
|
||||||
|
{
|
||||||
|
RectangleTrackingContainer {
|
||||||
|
id,
|
||||||
|
tx,
|
||||||
|
container: Container::new(content),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the [`Padding`] of the [`Container`].
|
||||||
|
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
||||||
|
self.container = self.container.padding(padding);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the width of the [`self.`].
|
||||||
|
pub fn width(mut self, width: Length) -> Self {
|
||||||
|
self.container = self.container.width(width);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the height of the [`Container`].
|
||||||
|
pub fn height(mut self, height: Length) -> Self {
|
||||||
|
self.container = self.container.height(height);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the maximum width of the [`Container`].
|
||||||
|
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||||
|
self.container = self.container.max_width(max_width);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the maximum height of the [`Container`] in pixels.
|
||||||
|
pub fn max_height(mut self, max_height: u32) -> Self {
|
||||||
|
self.container = self.container.max_height(max_height);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the content alignment for the horizontal axis of the [`Container`].
|
||||||
|
pub fn align_x(mut self, alignment: alignment::Horizontal) -> Self {
|
||||||
|
self.container = self.container.align_x(alignment);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the content alignment for the vertical axis of the [`Container`].
|
||||||
|
pub fn align_y(mut self, alignment: alignment::Vertical) -> Self {
|
||||||
|
self.container = self.container.align_y(alignment);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Centers the contents in the horizontal axis of the [`Container`].
|
||||||
|
pub fn center_x(mut self) -> Self {
|
||||||
|
self.container = self.container.center_x();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Centers the contents in the vertical axis of the [`Container`].
|
||||||
|
pub fn center_y(mut self) -> Self {
|
||||||
|
self.container = self.container.center_y();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the style of the [`Container`].
|
||||||
|
pub fn style(mut self, style: impl Into<<Renderer::Theme as StyleSheet>::Style>) -> Self {
|
||||||
|
self.container = self.container.style(style);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Renderer, I> Widget<Message, Renderer>
|
||||||
|
for RectangleTrackingContainer<'a, Message, Renderer, I>
|
||||||
|
where
|
||||||
|
Renderer: iced_native::Renderer,
|
||||||
|
Renderer::Theme: StyleSheet,
|
||||||
|
I: 'a + Hash + Copy + Send + Sync + Debug,
|
||||||
|
{
|
||||||
|
fn children(&self) -> Vec<Tree> {
|
||||||
|
self.container.children()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff(&self, tree: &mut Tree) {
|
||||||
|
self.container.diff(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn width(&self) -> Length {
|
||||||
|
Widget::width(&self.container)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn height(&self) -> Length {
|
||||||
|
Widget::height(&self.container)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node {
|
||||||
|
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 on_event(
|
||||||
|
&mut self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
event: Event,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor_position: Point,
|
||||||
|
renderer: &Renderer,
|
||||||
|
clipboard: &mut dyn Clipboard,
|
||||||
|
shell: &mut Shell<'_, Message>,
|
||||||
|
) -> event::Status {
|
||||||
|
self.container.on_event(
|
||||||
|
tree,
|
||||||
|
event,
|
||||||
|
layout,
|
||||||
|
cursor_position,
|
||||||
|
renderer,
|
||||||
|
clipboard,
|
||||||
|
shell,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_interaction(
|
||||||
|
&self,
|
||||||
|
tree: &Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor_position: Point,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
renderer: &Renderer,
|
||||||
|
) -> mouse::Interaction {
|
||||||
|
self.container
|
||||||
|
.mouse_interaction(tree, layout, cursor_position, viewport, renderer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
tree: &Tree,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
theme: &Renderer::Theme,
|
||||||
|
renderer_style: &renderer::Style,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor_position: Point,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
) {
|
||||||
|
let _ = self.tx.unbounded_send((self.id, layout.bounds()));
|
||||||
|
|
||||||
|
self.container.draw(
|
||||||
|
tree,
|
||||||
|
renderer,
|
||||||
|
theme,
|
||||||
|
renderer_style,
|
||||||
|
layout,
|
||||||
|
cursor_position,
|
||||||
|
viewport,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overlay<'b>(
|
||||||
|
&'b self,
|
||||||
|
tree: &'b mut Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||||
|
self.container.overlay(tree, layout, renderer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Renderer, I> From<RectangleTrackingContainer<'a, Message, Renderer, I>>
|
||||||
|
for Element<'a, Message, Renderer>
|
||||||
|
where
|
||||||
|
Message: 'a,
|
||||||
|
Renderer: 'a + iced_native::Renderer,
|
||||||
|
Renderer::Theme: StyleSheet,
|
||||||
|
I: 'a + Hash + Copy + Send + Sync + Debug,
|
||||||
|
{
|
||||||
|
fn from(
|
||||||
|
column: RectangleTrackingContainer<'a, Message, Renderer, I>,
|
||||||
|
) -> Element<'a, Message, Renderer> {
|
||||||
|
Element::new(column)
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/widget/rectangle_tracker/subscription.rs
Normal file
75
src/widget/rectangle_tracker/subscription.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
use iced::{
|
||||||
|
futures::{
|
||||||
|
channel::mpsc::{unbounded, UnboundedReceiver},
|
||||||
|
StreamExt,
|
||||||
|
},
|
||||||
|
subscription, Rectangle,
|
||||||
|
};
|
||||||
|
use std::{fmt::Debug, hash::Hash, collections::HashMap};
|
||||||
|
|
||||||
|
use super::RectangleTracker;
|
||||||
|
|
||||||
|
pub fn rectangle_tracker_subscription<
|
||||||
|
I: 'static + Hash + Copy + Send + Sync + Debug,
|
||||||
|
R: 'static + Hash + Copy + Send + Sync + Debug + Eq,
|
||||||
|
>(
|
||||||
|
id: I,
|
||||||
|
) -> iced::Subscription<(I, RectangleUpdate<R>)> {
|
||||||
|
subscription::unfold(id, State::Ready, move |state| start_listening(id, state))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum State<I> {
|
||||||
|
Ready,
|
||||||
|
Waiting(UnboundedReceiver<(I, Rectangle)>, HashMap<I, Rectangle>),
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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 || prev.height != new.height || prev.x != new.x || prev.y != new.y {
|
||||||
|
map.insert(u.0, new);
|
||||||
|
return (
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (None, State::Waiting(rx, map))
|
||||||
|
|
||||||
|
},
|
||||||
|
None => (None, State::Finished),
|
||||||
|
},
|
||||||
|
State::Finished => iced::futures::future::pending().await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum RectangleUpdate<I>
|
||||||
|
where
|
||||||
|
I: 'static + Hash + Copy + Send + Sync + Debug,
|
||||||
|
{
|
||||||
|
Rectangle((I, Rectangle)),
|
||||||
|
Init(RectangleTracker<I>),
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue