Compute panes regions and focus on click

This commit is contained in:
Héctor Ramón Jiménez 2020-03-04 22:01:57 +01:00
parent b6926d9ab4
commit d7f32d47ba
3 changed files with 190 additions and 19 deletions

View File

@ -12,6 +12,7 @@ pub fn main() {
})
}
#[derive(Debug)]
struct Launcher {
panes: panes::State<Example>,
}
@ -36,8 +37,6 @@ impl Application for Launcher {
let (clock, _) = Clock::new();
let (panes, _) = panes::State::new(Example::Clock(clock));
dbg!(&panes);
(Self { panes }, Command::none())
}
@ -61,6 +60,8 @@ impl Application for Launcher {
}
}
dbg!(self);
Command::none()
}

View File

@ -1,6 +1,7 @@
use crate::{
layout, Clipboard, Element, Event, Hasher, Layout, Length, Point, Size,
Widget,
input::{mouse, ButtonState},
layout, Clipboard, Element, Event, Hasher, Layout, Length, Point,
Rectangle, Size, Widget,
};
use std::collections::HashMap;
@ -8,7 +9,7 @@ use std::collections::HashMap;
#[allow(missing_debug_implementations)]
pub struct Panes<'a, Message, Renderer> {
state: &'a mut Internal,
elements: Vec<Element<'a, Message, Renderer>>,
elements: Vec<(Pane, Element<'a, Message, Renderer>)>,
width: Length,
height: Length,
}
@ -21,7 +22,7 @@ impl<'a, Message, Renderer> Panes<'a, Message, Renderer> {
let elements = state
.panes
.iter_mut()
.map(|(pane, state)| view(*pane, state))
.map(|(pane, state)| (*pane, view(*pane, state)))
.collect();
Self {
@ -71,15 +72,67 @@ where
let limits = limits.width(self.width).height(self.height);
let size = limits.resolve(Size::ZERO);
let regions = self.state.layout.regions(size);
let children = self
.elements
.iter()
.map(|element| element.layout(renderer, &limits))
.filter_map(|(pane, element)| {
let region = regions.get(pane)?;
let size = Size::new(region.width, region.height);
let mut node =
element.layout(renderer, &layout::Limits::new(size, size));
node.move_to(Point::new(region.x, region.y));
Some(node)
})
.collect();
layout::Node::with_children(size, children)
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
) {
match event {
Event::Mouse(mouse::Event::Input {
button: mouse::Button::Left,
state: ButtonState::Pressed,
}) => {
let mut clicked_region =
self.elements.iter().zip(layout.children()).filter(
|(_, layout)| layout.bounds().contains(cursor_position),
);
if let Some(((pane, _), _)) = clicked_region.next() {
self.state.focused_pane = Some(*pane);
}
}
_ => {}
}
self.elements.iter_mut().zip(layout.children()).for_each(
|((_, pane), layout)| {
pane.widget.on_event(
event.clone(),
layout,
cursor_position,
messages,
renderer,
clipboard,
)
},
);
}
fn draw(
&self,
renderer: &mut Renderer,
@ -98,7 +151,7 @@ where
self.height.hash(state);
self.state.layout.hash(state);
for element in &self.elements {
for (_, element) in &self.elements {
element.hash_layout(state);
}
}
@ -161,11 +214,7 @@ impl<T> State<T> {
}
pub fn split_vertically(&mut self, pane: &Pane, state: T) -> Option<Pane> {
let new_pane = Pane(self.internal.last_pane.checked_add(1)?);
// TODO
Some(new_pane)
self.split(Split::Vertical, pane, state)
}
pub fn split_horizontally(
@ -173,9 +222,22 @@ impl<T> State<T> {
pane: &Pane,
state: T,
) -> Option<Pane> {
let new_pane = Pane(self.internal.last_pane.checked_add(1)?);
self.split(Split::Horizontal, pane, state)
}
// TODO
fn split(&mut self, kind: Split, pane: &Pane, state: T) -> Option<Pane> {
let node = self.internal.layout.find(pane)?;
let new_pane = {
self.internal.last_pane = self.internal.last_pane.checked_add(1)?;
Pane(self.internal.last_pane)
};
node.split(kind, new_pane);
let _ = self.panes.insert(new_pane, state);
self.internal.focused_pane = Some(new_pane);
Some(new_pane)
}
@ -192,12 +254,120 @@ enum Node {
Pane(Pane),
}
impl Node {
pub fn find(&mut self, pane: &Pane) -> Option<&mut Node> {
match self {
Node::Split { a, b, .. } => {
if let Some(node) = a.find(pane) {
Some(node)
} else {
b.find(pane)
}
}
Node::Pane(p) => {
if p == pane {
Some(self)
} else {
None
}
}
}
}
pub fn split(&mut self, kind: Split, new_pane: Pane) {
*self = Node::Split {
kind,
ratio: 500_000,
a: Box::new(self.clone()),
b: Box::new(Node::Pane(new_pane)),
};
}
pub fn regions(&self, size: Size) -> HashMap<Pane, Rectangle> {
let mut regions = HashMap::new();
self.compute_regions(
&Rectangle {
x: 0.0,
y: 0.0,
width: size.width,
height: size.height,
},
&mut regions,
);
regions
}
fn compute_regions(
&self,
current: &Rectangle,
regions: &mut HashMap<Pane, Rectangle>,
) {
match self {
Node::Split { kind, ratio, a, b } => {
let ratio = *ratio as f32 / 1_000_000.0;
let (region_a, region_b) = kind.apply(current, ratio);
a.compute_regions(&region_a, regions);
b.compute_regions(&region_b, regions);
}
Node::Pane(pane) => {
let _ = regions.insert(*pane, *current);
}
}
}
}
#[derive(Debug, Clone, Copy, Hash)]
enum Split {
Horizontal,
Vertical,
}
impl Split {
pub fn apply(
&self,
rectangle: &Rectangle,
ratio: f32,
) -> (Rectangle, Rectangle) {
match self {
Split::Horizontal => {
let width_left = rectangle.width * ratio;
let width_right = rectangle.width - width_left;
(
Rectangle {
width: width_left,
..*rectangle
},
Rectangle {
x: rectangle.x + width_left,
width: width_right,
..*rectangle
},
)
}
Split::Vertical => {
let height_top = rectangle.height * ratio;
let height_bottom = rectangle.height - height_top;
(
Rectangle {
height: height_top,
..*rectangle
},
Rectangle {
x: rectangle.x + height_top,
height: height_bottom,
..*rectangle
},
)
}
}
}
}
/// The renderer of some [`Panes`].
///
/// Your [renderer] will need to implement this trait before being
@ -218,7 +388,7 @@ pub trait Renderer: crate::Renderer + Sized {
fn draw<Message>(
&mut self,
defaults: &Self::Defaults,
content: &[Element<'_, Message, Self>],
content: &[(Pane, Element<'_, Message, Self>)],
layout: Layout<'_>,
cursor_position: Point,
) -> Self::Output;

View File

@ -5,7 +5,7 @@ impl panes::Renderer for Renderer {
fn draw<Message>(
&mut self,
defaults: &Self::Defaults,
content: &[Element<'_, Message, Self>],
content: &[(panes::Pane, Element<'_, Message, Self>)],
layout: Layout<'_>,
cursor_position: Point,
) -> Self::Output {
@ -16,9 +16,9 @@ impl panes::Renderer for Renderer {
primitives: content
.iter()
.zip(layout.children())
.map(|(child, layout)| {
.map(|((_, pane), layout)| {
let (primitive, new_mouse_cursor) =
child.draw(self, defaults, layout, cursor_position);
pane.draw(self, defaults, layout, cursor_position);
if new_mouse_cursor > mouse_cursor {
mouse_cursor = new_mouse_cursor;