|
@@ -1,475 +0,0 @@
|
|
|
-use std::f32::consts::PI;
|
|
|
|
|
-
|
|
|
|
|
-use iced::{
|
|
|
|
|
- Alignment, Color, Element, Event,
|
|
|
|
|
- Length::{self, Fill},
|
|
|
|
|
- Point, Rectangle, Size, Subscription,
|
|
|
|
|
- advanced::{
|
|
|
|
|
- Layout, Widget,
|
|
|
|
|
- layout::{Limits, Node},
|
|
|
|
|
- renderer,
|
|
|
|
|
- widget::{Tree, tree},
|
|
|
|
|
- },
|
|
|
|
|
- border,
|
|
|
|
|
- mouse::{self, Interaction},
|
|
|
|
|
- widget::{column, container, row, text},
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-use crate::Message;
|
|
|
|
|
-
|
|
|
|
|
-pub fn subscribe() -> Subscription<Message> {
|
|
|
|
|
- // Subscribe to mouse events so we can inspect mouse movement and buttons for the resizable.
|
|
|
|
|
- iced::event::listen_with(|e, _status, _window_id| match e {
|
|
|
|
|
- Event::Mouse(e) => Some(Message::Resizable(ResizeableMessage::EventOccurred(e))),
|
|
|
|
|
- _ => None,
|
|
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-#[derive(Debug)]
|
|
|
|
|
-pub struct ResizableX {
|
|
|
|
|
- width: f32,
|
|
|
|
|
- hover_border: bool,
|
|
|
|
|
- dragging: bool,
|
|
|
|
|
-
|
|
|
|
|
- /// constraints
|
|
|
|
|
- min_width: f32,
|
|
|
|
|
- max_width: f32,
|
|
|
|
|
-
|
|
|
|
|
- /// last known mouse position (used while dragging)
|
|
|
|
|
- last_mouse_x: f32,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-impl ResizableX {
|
|
|
|
|
- pub fn new(initial_width: f32, min_width: f32, max_width: f32) -> Self {
|
|
|
|
|
- Self {
|
|
|
|
|
- width: initial_width,
|
|
|
|
|
- hover_border: false,
|
|
|
|
|
- dragging: false,
|
|
|
|
|
- min_width,
|
|
|
|
|
- max_width,
|
|
|
|
|
- last_mouse_x: 0.0,
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- pub fn update(&mut self, message: ResizeableMessage) {
|
|
|
|
|
- match message {
|
|
|
|
|
- ResizeableMessage::EventOccurred(event) => {
|
|
|
|
|
- // We're only interested in mouse events here
|
|
|
|
|
- match event {
|
|
|
|
|
- mouse::Event::CursorMoved { position } => {
|
|
|
|
|
- let x = position.x;
|
|
|
|
|
- self.last_mouse_x = x;
|
|
|
|
|
- // hover region: within 6 px of the current right border
|
|
|
|
|
- let near_border = (x - self.width).abs() <= 6.0;
|
|
|
|
|
-
|
|
|
|
|
- // if already dragging, update width according to cursor x
|
|
|
|
|
- if self.dragging {
|
|
|
|
|
- // clamp width between min and max
|
|
|
|
|
- let new_w = x.clamp(self.min_width as f32, self.max_width as f32);
|
|
|
|
|
- self.width = new_w.round();
|
|
|
|
|
- self.last_mouse_x = x;
|
|
|
|
|
- // keep hover true while dragging to show consistent cursor feedback
|
|
|
|
|
- self.hover_border = true;
|
|
|
|
|
- } else {
|
|
|
|
|
- // only update hover when not dragging
|
|
|
|
|
- self.hover_border = near_border;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- mouse::Event::ButtonPressed(mouse::Button::Left) => {
|
|
|
|
|
- // Start dragging if the left mouse button pressed while cursor near border
|
|
|
|
|
- // Note: We rely on last cursor position reported in CursorMoved events.
|
|
|
|
|
- // If there was no previous CursorMoved, last_mouse_x might be 0; that's fine.
|
|
|
|
|
- let x = self.last_mouse_x;
|
|
|
|
|
- if (x - (self.width as f32)).abs() <= 6.0 {
|
|
|
|
|
- self.dragging = true;
|
|
|
|
|
- } else {
|
|
|
|
|
- // If clicking inside the panel area but not on border, nothing special
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- mouse::Event::ButtonReleased(mouse::Button::Left) => {
|
|
|
|
|
- // stop dragging
|
|
|
|
|
- if self.dragging {
|
|
|
|
|
- self.dragging = false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- _ => {}
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ResizeableMessage::UpdateHover(b) => {
|
|
|
|
|
- self.hover_border = b;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- pub fn view<'a, L, R, M>(&'a self, left: L, right: R) -> Element<'a, M>
|
|
|
|
|
- where
|
|
|
|
|
- L: Into<Element<'a, M>>,
|
|
|
|
|
- R: Into<Element<'a, M>>,
|
|
|
|
|
- M: 'a,
|
|
|
|
|
- {
|
|
|
|
|
- // handle visuals: when hovering or dragging, show thicker line
|
|
|
|
|
- let handle: Element<'a, M> = {
|
|
|
|
|
- container(" ")
|
|
|
|
|
- .width(Length::Fixed(6.))
|
|
|
|
|
- .height(Length::Fill)
|
|
|
|
|
- .style(|_| iced::widget::container::Style {
|
|
|
|
|
- text_color: Some(iced::Color::from_rgba(1., 1., 1., 0.7)),
|
|
|
|
|
- background: None,
|
|
|
|
|
- border: iced::Border {
|
|
|
|
|
- color: iced::Color::WHITE,
|
|
|
|
|
- width: 1.,
|
|
|
|
|
- radius: (PI / 2.).into(),
|
|
|
|
|
- },
|
|
|
|
|
- shadow: iced::Shadow::default(),
|
|
|
|
|
- snap: true,
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- .into();
|
|
|
|
|
-
|
|
|
|
|
- let left: Element<'a, M> = left.into();
|
|
|
|
|
- let right: Element<'a, M> = right.into();
|
|
|
|
|
-
|
|
|
|
|
- let left: Element<'a, M> = row![left, handle]
|
|
|
|
|
- .width(Length::Fixed(self.width))
|
|
|
|
|
- .height(Length::Fill)
|
|
|
|
|
- .spacing(0)
|
|
|
|
|
- .into();
|
|
|
|
|
-
|
|
|
|
|
- let content = row![left, right].width(Length::Fill).height(Length::Fill);
|
|
|
|
|
-
|
|
|
|
|
- container(content)
|
|
|
|
|
- .width(Length::Fill)
|
|
|
|
|
- .height(Length::Fill)
|
|
|
|
|
- .into()
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// #[derive(Debug)]
|
|
|
|
|
-pub struct ResizableY<'a, Message, Theme, Renderer> {
|
|
|
|
|
- height: f32,
|
|
|
|
|
- hover_border: bool,
|
|
|
|
|
- dragging: bool,
|
|
|
|
|
-
|
|
|
|
|
- up: Element<'a, Message, Theme, Renderer>,
|
|
|
|
|
- down: Element<'a, Message, Theme, Renderer>,
|
|
|
|
|
-
|
|
|
|
|
- /// constraints
|
|
|
|
|
- min_height: f32,
|
|
|
|
|
- max_height: f32,
|
|
|
|
|
-
|
|
|
|
|
- /// last known mouse position (used while dragging)
|
|
|
|
|
- last_mouse_y: f32,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-struct ResizableState;
|
|
|
|
|
-
|
|
|
|
|
-impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
|
|
|
|
- for ResizableY<'a, Message, Theme, Renderer>
|
|
|
|
|
-where
|
|
|
|
|
- Renderer: iced::advanced::Renderer,
|
|
|
|
|
-{
|
|
|
|
|
- fn size(&self) -> iced::Size<Length> {
|
|
|
|
|
- iced::Size::new(Fill, Fill)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn size_hint(&self) -> Size<Length> {
|
|
|
|
|
- self.size()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn layout(
|
|
|
|
|
- &mut self,
|
|
|
|
|
- tree: &mut iced::advanced::widget::Tree,
|
|
|
|
|
- renderer: &Renderer,
|
|
|
|
|
- limits: &iced::advanced::layout::Limits,
|
|
|
|
|
- ) -> iced::advanced::layout::Node {
|
|
|
|
|
- let size = limits.max();
|
|
|
|
|
-
|
|
|
|
|
- let up_node = self
|
|
|
|
|
- .up
|
|
|
|
|
- .as_widget_mut()
|
|
|
|
|
- .layout(&mut tree.children[0], renderer, limits);
|
|
|
|
|
-
|
|
|
|
|
- let up_total = up_node.bounds().y + up_node.bounds().height;
|
|
|
|
|
-
|
|
|
|
|
- let down_node = self
|
|
|
|
|
- .down
|
|
|
|
|
- .as_widget_mut()
|
|
|
|
|
- .layout(
|
|
|
|
|
- &mut tree.children[1],
|
|
|
|
|
- renderer,
|
|
|
|
|
- &limits.max_height(size.height - up_total),
|
|
|
|
|
- )
|
|
|
|
|
- .move_to(Point::new(0.0, up_total));
|
|
|
|
|
-
|
|
|
|
|
- let node = Node::with_children(size, vec![up_node, down_node]);
|
|
|
|
|
-
|
|
|
|
|
- node
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn draw(
|
|
|
|
|
- &self,
|
|
|
|
|
- tree: &iced::advanced::widget::Tree,
|
|
|
|
|
- renderer: &mut Renderer,
|
|
|
|
|
- theme: &Theme,
|
|
|
|
|
- style: &iced::advanced::renderer::Style,
|
|
|
|
|
- layout: iced::advanced::Layout<'_>,
|
|
|
|
|
- cursor: iced::advanced::mouse::Cursor,
|
|
|
|
|
- viewport: &iced::Rectangle,
|
|
|
|
|
- ) {
|
|
|
|
|
- let mut children = layout.children();
|
|
|
|
|
- let up = children.next().unwrap();
|
|
|
|
|
- let down = children.next().unwrap();
|
|
|
|
|
-
|
|
|
|
|
- // Draw the upper child
|
|
|
|
|
- self.up.as_widget().draw(
|
|
|
|
|
- &tree.children[0],
|
|
|
|
|
- renderer,
|
|
|
|
|
- theme,
|
|
|
|
|
- style,
|
|
|
|
|
- up,
|
|
|
|
|
- cursor,
|
|
|
|
|
- viewport,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- // Draw the divider bar
|
|
|
|
|
- let divider_bounds = Rectangle {
|
|
|
|
|
- x: up.bounds().x,
|
|
|
|
|
- y: up.bounds().y + up.bounds().height,
|
|
|
|
|
- width: up.bounds().width,
|
|
|
|
|
- height: 20.0,
|
|
|
|
|
- };
|
|
|
|
|
- renderer.fill_quad(
|
|
|
|
|
- renderer::Quad {
|
|
|
|
|
- bounds: divider_bounds,
|
|
|
|
|
- border: border::rounded(0.0),
|
|
|
|
|
- ..Default::default()
|
|
|
|
|
- },
|
|
|
|
|
- Color::from_rgb(0.5, 0.5, 0.2),
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- // Draw the lower child
|
|
|
|
|
- self.down.as_widget().draw(
|
|
|
|
|
- &tree.children[1],
|
|
|
|
|
- renderer,
|
|
|
|
|
- theme,
|
|
|
|
|
- style,
|
|
|
|
|
- down,
|
|
|
|
|
- cursor,
|
|
|
|
|
- viewport,
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn tag(&self) -> tree::Tag {
|
|
|
|
|
- tree::Tag::of::<ResizableState>()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn state(&self) -> tree::State {
|
|
|
|
|
- tree::State::new(ResizableState)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn children(&self) -> Vec<Tree> {
|
|
|
|
|
- vec![Tree::new(&self.up), Tree::new(&self.down)]
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn diff(&self, tree: &mut Tree) {
|
|
|
|
|
- tree.diff_children(&[&self.up, &self.down]);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn update(
|
|
|
|
|
- &mut self,
|
|
|
|
|
- state: &mut Tree,
|
|
|
|
|
- event: &Event,
|
|
|
|
|
- layout: Layout<'_>,
|
|
|
|
|
- cursor: iced::advanced::mouse::Cursor,
|
|
|
|
|
- renderer: &Renderer,
|
|
|
|
|
- clipboard: &mut dyn iced::advanced::Clipboard,
|
|
|
|
|
- shell: &mut iced::advanced::Shell<'_, Message>,
|
|
|
|
|
- viewport: &Rectangle,
|
|
|
|
|
- ) {
|
|
|
|
|
- let mut children = layout.children();
|
|
|
|
|
-
|
|
|
|
|
- self.up.as_widget_mut().update(
|
|
|
|
|
- &mut state.children[0],
|
|
|
|
|
- event,
|
|
|
|
|
- children.next().unwrap(),
|
|
|
|
|
- cursor,
|
|
|
|
|
- renderer,
|
|
|
|
|
- clipboard,
|
|
|
|
|
- shell,
|
|
|
|
|
- viewport,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- self.down.as_widget_mut().update(
|
|
|
|
|
- &mut state.children[1],
|
|
|
|
|
- event,
|
|
|
|
|
- children.next().unwrap(),
|
|
|
|
|
- cursor,
|
|
|
|
|
- renderer,
|
|
|
|
|
- clipboard,
|
|
|
|
|
- shell,
|
|
|
|
|
- viewport,
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- fn mouse_interaction(
|
|
|
|
|
- &self,
|
|
|
|
|
- tree: &Tree,
|
|
|
|
|
- layout: Layout<'_>,
|
|
|
|
|
- cursor: mouse::Cursor,
|
|
|
|
|
- viewport: &Rectangle,
|
|
|
|
|
- renderer: &Renderer,
|
|
|
|
|
- ) -> mouse::Interaction {
|
|
|
|
|
- let mut children = layout.children();
|
|
|
|
|
-
|
|
|
|
|
- let up = self.up.as_widget().mouse_interaction(
|
|
|
|
|
- &tree.children[0],
|
|
|
|
|
- children.next().unwrap(),
|
|
|
|
|
- cursor,
|
|
|
|
|
- viewport,
|
|
|
|
|
- renderer,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- match up {
|
|
|
|
|
- Interaction::None => {}
|
|
|
|
|
- _ => return up,
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- let down = self.down.as_widget().mouse_interaction(
|
|
|
|
|
- &tree.children[1],
|
|
|
|
|
- children.next().unwrap(),
|
|
|
|
|
- cursor,
|
|
|
|
|
- viewport,
|
|
|
|
|
- renderer,
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- match down {
|
|
|
|
|
- Interaction::None => {}
|
|
|
|
|
- _ => return down,
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- mouse::Interaction::default()
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-impl<'a, Message, Theme, Renderer> From<ResizableY<'a, Message, Theme, Renderer>>
|
|
|
|
|
- for Element<'a, Message, Theme, Renderer>
|
|
|
|
|
-where
|
|
|
|
|
- Message: Clone + 'a,
|
|
|
|
|
- Theme: 'a,
|
|
|
|
|
- Renderer: iced::advanced::Renderer + 'a,
|
|
|
|
|
-{
|
|
|
|
|
- fn from(value: ResizableY<'a, Message, Theme, Renderer>) -> Self {
|
|
|
|
|
- Self::new(value)
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-impl<'a, Message, Theme, Renderer> ResizableY<'a, Message, Theme, Renderer> {
|
|
|
|
|
- pub fn new(
|
|
|
|
|
- up: Element<'a, Message, Theme, Renderer>,
|
|
|
|
|
- down: Element<'a, Message, Theme, Renderer>,
|
|
|
|
|
- ) -> Self {
|
|
|
|
|
- Self {
|
|
|
|
|
- height: 500.,
|
|
|
|
|
- hover_border: false,
|
|
|
|
|
- dragging: false,
|
|
|
|
|
- min_height: 400.,
|
|
|
|
|
- max_height: 800.,
|
|
|
|
|
- last_mouse_y: 0.0,
|
|
|
|
|
- up,
|
|
|
|
|
- down,
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- pub fn update(&mut self, message: ResizeableMessage) {
|
|
|
|
|
- // dbg!(&self, &message);
|
|
|
|
|
- match message {
|
|
|
|
|
- ResizeableMessage::EventOccurred(event) => {
|
|
|
|
|
- // We're only interested in mouse events here
|
|
|
|
|
- match event {
|
|
|
|
|
- mouse::Event::CursorMoved { position } => {
|
|
|
|
|
- let y = position.y;
|
|
|
|
|
- self.last_mouse_y = y;
|
|
|
|
|
- // hover region: within 6 px of the current right border
|
|
|
|
|
- let near_border = (y - self.height).abs() <= 6.0;
|
|
|
|
|
-
|
|
|
|
|
- // if already dragging, update width according to cursor x
|
|
|
|
|
- if self.dragging {
|
|
|
|
|
- // clamp width between min and max
|
|
|
|
|
- let new_h = y.clamp(self.min_height as f32, self.max_height as f32);
|
|
|
|
|
- self.height = new_h.round();
|
|
|
|
|
- self.last_mouse_y = y;
|
|
|
|
|
- // keep hover true while dragging to show consistent cursor feedback
|
|
|
|
|
- self.hover_border = true;
|
|
|
|
|
- } else {
|
|
|
|
|
- // only update hover when not dragging
|
|
|
|
|
- self.hover_border = near_border;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- mouse::Event::ButtonPressed(mouse::Button::Left) => {
|
|
|
|
|
- let y = self.last_mouse_y;
|
|
|
|
|
- if (y - (self.height as f32)).abs() <= 6.0 {
|
|
|
|
|
- self.dragging = true;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- mouse::Event::ButtonReleased(mouse::Button::Left) => {
|
|
|
|
|
- // stop dragging
|
|
|
|
|
- if self.dragging {
|
|
|
|
|
- self.dragging = false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- _ => {}
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ResizeableMessage::UpdateHover(b) => {
|
|
|
|
|
- self.hover_border = b;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- pub fn view(&'a self) -> Element<'a, Message> {
|
|
|
|
|
- let handle_width = 6_f32;
|
|
|
|
|
-
|
|
|
|
|
- // handle visuals: when hovering or dragging, show thicker line
|
|
|
|
|
- let handle: Element<'a, Message> = {
|
|
|
|
|
- container(" ")
|
|
|
|
|
- .height(Length::Fixed(handle_width))
|
|
|
|
|
- .width(Length::Fill)
|
|
|
|
|
- .style(|_| iced::widget::container::Style {
|
|
|
|
|
- text_color: Some(iced::Color::from_rgba(1., 1., 1., 0.7)),
|
|
|
|
|
- background: None,
|
|
|
|
|
- border: iced::Border {
|
|
|
|
|
- color: iced::Color::WHITE,
|
|
|
|
|
- width: 1.,
|
|
|
|
|
- radius: (PI / 2.).into(),
|
|
|
|
|
- },
|
|
|
|
|
- shadow: iced::Shadow::default(),
|
|
|
|
|
- snap: true,
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- .into();
|
|
|
|
|
-
|
|
|
|
|
- // let up: Element<'a, Message> = column![self.up, handle]
|
|
|
|
|
- // .width(Fill)
|
|
|
|
|
- // .height(Length::Fixed(self.height))
|
|
|
|
|
- // .spacing(0)
|
|
|
|
|
- // .into();
|
|
|
|
|
- //
|
|
|
|
|
- // let content = column![up, self.down]
|
|
|
|
|
- // .width(Length::Fill)
|
|
|
|
|
- // .height(Length::Fill);
|
|
|
|
|
-
|
|
|
|
|
- container("")
|
|
|
|
|
- .width(Length::Fill)
|
|
|
|
|
- .height(Length::Fill)
|
|
|
|
|
- .into()
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-#[derive(Debug, Clone)]
|
|
|
|
|
-pub enum ResizeableMessage {
|
|
|
|
|
- /// A raw iced-native event so we can inspect mouse move/press/release
|
|
|
|
|
- EventOccurred(iced::mouse::Event),
|
|
|
|
|
-
|
|
|
|
|
- /// TODO: update hover state when hovering sidebar with mouse
|
|
|
|
|
- UpdateHover(bool),
|
|
|
|
|
-}
|
|
|