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::{
|
||||
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,
|
||||
theme::{self, Theme},
|
||||
widget::{button, nav_button, nav_bar, nav_bar_page, nav_bar_section, header_bar, settings, scrollable, toggler},
|
||||
Element,
|
||||
ElementExt,
|
||||
widget::{
|
||||
button, header_bar, nav_bar, nav_bar_page, nav_bar_section, nav_button, scrollable,
|
||||
settings, toggler, rectangle_tracker::{RectangleTracker, rectangle_tracker_subscription, RectangleUpdate},
|
||||
},
|
||||
Element, ElementExt
|
||||
};
|
||||
use std::{collections::BTreeMap, vec};
|
||||
use theme::Button as ButtonTheme;
|
||||
|
|
@ -29,6 +31,7 @@ pub struct Window {
|
|||
show_minimize: bool,
|
||||
show_maximize: bool,
|
||||
exit: bool,
|
||||
rectangle_tracker: Option<RectangleTracker<u32>>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
|
@ -49,7 +52,7 @@ impl Window {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
Page(u8),
|
||||
Debug(bool),
|
||||
|
|
@ -66,6 +69,7 @@ pub enum Message {
|
|||
Minimize,
|
||||
Maximize,
|
||||
InputChanged,
|
||||
Rectangle(RectangleUpdate<u32>)
|
||||
}
|
||||
|
||||
impl Application for Window {
|
||||
|
|
@ -99,7 +103,7 @@ impl Application for Window {
|
|||
Message::SliderChanged(value) => self.slider_value = value,
|
||||
Message::CheckboxToggled(value) => {
|
||||
self.checkbox_value = value;
|
||||
},
|
||||
}
|
||||
Message::TogglerToggled(value) => self.toggler_value = value,
|
||||
Message::PickListSelected(value) => self.pick_list_selected = Some(value),
|
||||
Message::Close => self.exit = true,
|
||||
|
|
@ -108,8 +112,11 @@ impl Application for Window {
|
|||
Message::Minimize => todo!(),
|
||||
Message::Maximize => todo!(),
|
||||
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()
|
||||
|
|
@ -124,7 +131,7 @@ impl Application for Window {
|
|||
nav_button("Settings")
|
||||
.on_sidebar_toggled(Message::ToggleSidebar)
|
||||
.sidebar_active(self.sidebar_toggled)
|
||||
.into()
|
||||
.into(),
|
||||
);
|
||||
|
||||
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![
|
||||
settings::view_section("Debug")
|
||||
.add(settings::item("Debug theme", choose_theme))
|
||||
.add(settings::item(
|
||||
"Debug layout",
|
||||
toggler(String::from("Debug layout"), self.debug, Message::Debug)
|
||||
toggler(String::from("Debug layout"), self.debug, Message::Debug),
|
||||
))
|
||||
.into(),
|
||||
settings::view_section("Buttons")
|
||||
|
|
@ -241,10 +256,7 @@ impl Application for Window {
|
|||
.text("Primary")
|
||||
.on_press(Message::ButtonPressed)
|
||||
.into(),
|
||||
button(ButtonTheme::Secondary)
|
||||
.text("Secondary")
|
||||
.on_press(Message::ButtonPressed)
|
||||
.into(),
|
||||
secondary,
|
||||
button(ButtonTheme::Positive)
|
||||
.text("Positive")
|
||||
.on_press(Message::ButtonPressed)
|
||||
|
|
@ -256,7 +268,7 @@ impl Application for Window {
|
|||
button(ButtonTheme::Text)
|
||||
.text("Text")
|
||||
.on_press(Message::ButtonPressed)
|
||||
.into()
|
||||
.into(),
|
||||
]))
|
||||
.add(settings::item_row(vec![
|
||||
button(ButtonTheme::Primary).text("Primary").into(),
|
||||
|
|
@ -267,28 +279,31 @@ impl Application for Window {
|
|||
]))
|
||||
.into(),
|
||||
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(
|
||||
"Pick List (TODO)",
|
||||
pick_list(
|
||||
vec!["Option 1", "Option 2", "Option 3", "Option 4",],
|
||||
vec!["Option 1", "Option 2", "Option 3", "Option 4"],
|
||||
self.pick_list_selected,
|
||||
Message::PickListSelected
|
||||
Message::PickListSelected,
|
||||
)
|
||||
.padding([8, 0, 8, 16])
|
||||
.padding([8, 0, 8, 16]),
|
||||
))
|
||||
.add(settings::item(
|
||||
"Slider",
|
||||
slider(0.0..=100.0, self.slider_value, Message::SliderChanged)
|
||||
.width(Length::Units(250))
|
||||
.width(Length::Units(250)),
|
||||
))
|
||||
.add(settings::item(
|
||||
"Progress",
|
||||
progress_bar(0.0..=100.0, self.slider_value)
|
||||
.width(Length::Units(250))
|
||||
.height(Length::Units(4))
|
||||
.height(Length::Units(4)),
|
||||
))
|
||||
.into()
|
||||
.into(),
|
||||
])
|
||||
.into();
|
||||
|
||||
|
|
@ -323,8 +338,11 @@ impl Application for Window {
|
|||
fn theme(&self) -> Theme {
|
||||
self.theme
|
||||
}
|
||||
|
||||
|
||||
fn close_requested(&self, id: SurfaceIdWrapper) -> Self::Message {
|
||||
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 use navigation::*;
|
||||
|
||||
pub mod popup;
|
||||
pub use popup::*;
|
||||
|
||||
mod toggler;
|
||||
pub use toggler::toggler;
|
||||
|
||||
|
|
@ -34,4 +31,6 @@ pub mod separator;
|
|||
pub use separator::{horizontal_rule, vertical_rule};
|
||||
|
||||
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