mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-24 01:46:31 +00:00
Add mouse support to Dialog and Button
This commit is contained in:
parent
ba11200341
commit
f3528088cd
@ -28,7 +28,8 @@ pub struct Button {
|
|||||||
impl Button {
|
impl Button {
|
||||||
/// Creates a new button with the given content and callback.
|
/// Creates a new button with the given content and callback.
|
||||||
pub fn new<F, S: Into<String>>(label: S, cb: F) -> Self
|
pub fn new<F, S: Into<String>>(label: S, cb: F) -> Self
|
||||||
where F: Fn(&mut Cursive) + 'static
|
where
|
||||||
|
F: Fn(&mut Cursive) + 'static,
|
||||||
{
|
{
|
||||||
Button {
|
Button {
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
@ -41,7 +42,8 @@ impl Button {
|
|||||||
///
|
///
|
||||||
/// Replaces the previous callback.
|
/// Replaces the previous callback.
|
||||||
pub fn set_callback<F>(&mut self, cb: F)
|
pub fn set_callback<F>(&mut self, cb: F)
|
||||||
where F: Fn(&mut Cursive) + 'static
|
where
|
||||||
|
F: Fn(&mut Cursive) + 'static,
|
||||||
{
|
{
|
||||||
self.callback = Callback::from_fn(cb);
|
self.callback = Callback::from_fn(cb);
|
||||||
}
|
}
|
||||||
@ -74,11 +76,14 @@ impl Button {
|
|||||||
pub fn is_enabled(&self) -> bool {
|
pub fn is_enabled(&self) -> bool {
|
||||||
self.enabled
|
self.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn req_size(&self) -> Vec2 {
|
||||||
|
Vec2::new(2 + self.label.width(), 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View for Button {
|
impl View for Button {
|
||||||
fn draw(&self, printer: &Printer) {
|
fn draw(&self, printer: &Printer) {
|
||||||
|
|
||||||
if printer.size.x == 0 {
|
if printer.size.x == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -101,15 +106,25 @@ impl View for Button {
|
|||||||
|
|
||||||
fn required_size(&mut self, _: Vec2) -> Vec2 {
|
fn required_size(&mut self, _: Vec2) -> Vec2 {
|
||||||
// Meh. Fixed size we are.
|
// Meh. Fixed size we are.
|
||||||
Vec2::new(2 + self.label.width(), 1)
|
self.req_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
|
eprintln!("{:?}", event);
|
||||||
|
eprintln!("{:?}", self.req_size());
|
||||||
match event {
|
match event {
|
||||||
// 10 is the ascii code for '\n', that is the return key
|
// 10 is the ascii code for '\n', that is the return key
|
||||||
Event::Key(Key::Enter) => {
|
Event::Key(Key::Enter) => {
|
||||||
EventResult::Consumed(Some(self.callback.clone()))
|
EventResult::Consumed(Some(self.callback.clone()))
|
||||||
}
|
}
|
||||||
|
Event::Mouse {
|
||||||
|
event: MouseEvent::Release(MouseButton::Left),
|
||||||
|
position,
|
||||||
|
offset,
|
||||||
|
} if position.fits_in_rect(offset, self.req_size()) =>
|
||||||
|
{
|
||||||
|
EventResult::Consumed(Some(self.callback.clone()))
|
||||||
|
}
|
||||||
_ => EventResult::Ignored,
|
_ => EventResult::Ignored,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
use Cursive;
|
use Cursive;
|
||||||
use Printer;
|
use Printer;
|
||||||
use With;
|
use With;
|
||||||
@ -7,9 +5,9 @@ use align::*;
|
|||||||
use direction::Direction;
|
use direction::Direction;
|
||||||
use event::*;
|
use event::*;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::cell::Cell;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use theme::ColorStyle;
|
use theme::ColorStyle;
|
||||||
|
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
use vec::{Vec2, Vec4};
|
use vec::{Vec2, Vec4};
|
||||||
use view::{Selector, View};
|
use view::{Selector, View};
|
||||||
@ -21,6 +19,23 @@ enum Focus {
|
|||||||
Button(usize),
|
Button(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ChildButton {
|
||||||
|
button: SizedView<Button>,
|
||||||
|
offset: Cell<Vec2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChildButton {
|
||||||
|
pub fn new<F, S: Into<String>>(label: S, cb: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&mut Cursive) + 'static,
|
||||||
|
{
|
||||||
|
ChildButton {
|
||||||
|
button: SizedView::new(Button::new(label, cb)),
|
||||||
|
offset: Cell::new(Vec2::zero()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Popup-like view with a main content, and optional buttons under it.
|
/// Popup-like view with a main content, and optional buttons under it.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@ -31,16 +46,26 @@ enum Focus {
|
|||||||
/// .button("Ok", |s| s.quit());
|
/// .button("Ok", |s| s.quit());
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Dialog {
|
pub struct Dialog {
|
||||||
|
// Possibly empty title.
|
||||||
title: String,
|
title: String,
|
||||||
content: Box<View>,
|
|
||||||
|
|
||||||
buttons: Vec<SizedView<Button>>,
|
// The actual inner view.
|
||||||
|
content: SizedView<Box<View>>,
|
||||||
|
|
||||||
|
// Optional list of buttons under the main view.
|
||||||
|
// Include the top-left corner.
|
||||||
|
buttons: Vec<ChildButton>,
|
||||||
|
|
||||||
|
// Padding around the inner view.
|
||||||
padding: Vec4,
|
padding: Vec4,
|
||||||
|
|
||||||
|
// Borders around everything.
|
||||||
borders: Vec4,
|
borders: Vec4,
|
||||||
|
|
||||||
|
// The current element in focus
|
||||||
focus: Focus,
|
focus: Focus,
|
||||||
|
|
||||||
|
// How to align the buttons under the view.
|
||||||
align: Align,
|
align: Align,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +82,7 @@ impl Dialog {
|
|||||||
/// Creates a new `Dialog` with the given content.
|
/// Creates a new `Dialog` with the given content.
|
||||||
pub fn around<V: View + 'static>(view: V) -> Self {
|
pub fn around<V: View + 'static>(view: V) -> Self {
|
||||||
Dialog {
|
Dialog {
|
||||||
content: Box::new(view),
|
content: SizedView::new(Box::new(view)),
|
||||||
buttons: Vec::new(),
|
buttons: Vec::new(),
|
||||||
title: String::new(),
|
title: String::new(),
|
||||||
focus: Focus::Content,
|
focus: Focus::Content,
|
||||||
@ -78,7 +103,7 @@ impl Dialog {
|
|||||||
///
|
///
|
||||||
/// Previous content will be dropped.
|
/// Previous content will be dropped.
|
||||||
pub fn set_content<V: View + 'static>(&mut self, view: V) {
|
pub fn set_content<V: View + 'static>(&mut self, view: V) {
|
||||||
self.content = Box::new(view);
|
self.content = SizedView::new(Box::new(view));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenient method to create a dialog with a simple text content.
|
/// Convenient method to create a dialog with a simple text content.
|
||||||
@ -97,9 +122,10 @@ impl Dialog {
|
|||||||
///
|
///
|
||||||
/// Consumes and returns self for easy chaining.
|
/// Consumes and returns self for easy chaining.
|
||||||
pub fn button<F, S: Into<String>>(mut self, label: S, cb: F) -> Self
|
pub fn button<F, S: Into<String>>(mut self, label: S, cb: F) -> Self
|
||||||
where F: Fn(&mut Cursive) + 'static
|
where
|
||||||
|
F: Fn(&mut Cursive) + 'static,
|
||||||
{
|
{
|
||||||
self.buttons.push(SizedView::new(Button::new(label, cb)));
|
self.buttons.push(ChildButton::new(label, cb));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -113,6 +139,9 @@ impl Dialog {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Commented out because currently un-implemented.
|
||||||
|
*
|
||||||
/// Sets the vertical alignment for the buttons, if any.
|
/// Sets the vertical alignment for the buttons, if any.
|
||||||
///
|
///
|
||||||
/// Only works if the buttons are as a column to the right of the dialog.
|
/// Only works if the buttons are as a column to the right of the dialog.
|
||||||
@ -121,6 +150,7 @@ impl Dialog {
|
|||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/// Shortcut method to add a button that will dismiss the dialog.
|
/// Shortcut method to add a button that will dismiss the dialog.
|
||||||
pub fn dismiss_button<S: Into<String>>(self, label: S) -> Self {
|
pub fn dismiss_button<S: Into<String>>(self, label: S) -> Self {
|
||||||
@ -169,150 +199,13 @@ impl Dialog {
|
|||||||
self.padding.right = padding;
|
self.padding.right = padding;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl View for Dialog {
|
|
||||||
fn draw(&self, printer: &Printer) {
|
|
||||||
|
|
||||||
// This will be the buttons_height used by the buttons.
|
// Private methods
|
||||||
let mut buttons_height = 0;
|
|
||||||
// Current horizontal position of the next button we'll draw.
|
|
||||||
|
|
||||||
// Sum of the sizes + len-1 for margins
|
// An event is received while the content is in focus
|
||||||
let width = self.buttons
|
fn on_event_content(&mut self, event: Event) -> EventResult {
|
||||||
.iter()
|
match self.content.on_event(event.relativized((1, 1))) {
|
||||||
.map(|button| button.size.x)
|
|
||||||
.fold(0, |a, b| a + b) +
|
|
||||||
self.buttons.len().saturating_sub(1);
|
|
||||||
let overhead = self.padding + self.borders;
|
|
||||||
if printer.size.x < overhead.horizontal() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let mut offset = overhead.left +
|
|
||||||
self.align.h.get_offset(width,
|
|
||||||
printer.size.x -
|
|
||||||
overhead.horizontal());
|
|
||||||
|
|
||||||
let overhead_bottom = self.padding.bottom + self.borders.bottom + 1;
|
|
||||||
|
|
||||||
let y = match printer.size.y.checked_sub(overhead_bottom) {
|
|
||||||
Some(y) => y,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (i, button) in self.buttons.iter().enumerate() {
|
|
||||||
let size = button.size;
|
|
||||||
// Add some special effect to the focused button
|
|
||||||
button.draw(&printer.sub_printer(Vec2::new(offset, y),
|
|
||||||
size,
|
|
||||||
self.focus == Focus::Button(i)));
|
|
||||||
// Keep 1 blank between two buttons
|
|
||||||
offset += size.x + 1;
|
|
||||||
// Also keep 1 blank above the buttons
|
|
||||||
buttons_height = max(buttons_height, size.y + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// What do we have left?
|
|
||||||
let taken = Vec2::new(0, buttons_height) + self.borders.combined() +
|
|
||||||
self.padding.combined();
|
|
||||||
|
|
||||||
let inner_size = match printer.size.checked_sub(taken) {
|
|
||||||
Some(s) => s,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.content.draw(&printer.sub_printer(self.borders.top_left() +
|
|
||||||
self.padding.top_left(),
|
|
||||||
inner_size,
|
|
||||||
self.focus == Focus::Content));
|
|
||||||
|
|
||||||
printer.print_box(Vec2::new(0, 0), printer.size, false);
|
|
||||||
|
|
||||||
if !self.title.is_empty() {
|
|
||||||
let len = self.title.width();
|
|
||||||
if len + 4 > printer.size.x {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let x = (printer.size.x - len) / 2;
|
|
||||||
printer.with_high_border(false, |printer| {
|
|
||||||
printer.print((x - 2, 0), "┤ ");
|
|
||||||
printer.print((x + len, 0), " ├");
|
|
||||||
});
|
|
||||||
|
|
||||||
printer.with_color(ColorStyle::TitlePrimary,
|
|
||||||
|p| p.print((x, 0), &self.title));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
|
||||||
// Padding and borders are not available for kids.
|
|
||||||
let nomans_land = self.padding.combined() + self.borders.combined();
|
|
||||||
|
|
||||||
// Buttons are not flexible, so their size doesn't depend on ours.
|
|
||||||
let mut buttons_size = Vec2::new(0, 0);
|
|
||||||
|
|
||||||
// Start with the inter-button space.
|
|
||||||
buttons_size.x += self.buttons.len().saturating_sub(1);
|
|
||||||
|
|
||||||
for button in &mut self.buttons {
|
|
||||||
let s = button.view.required_size(req);
|
|
||||||
buttons_size.x += s.x;
|
|
||||||
buttons_size.y = max(buttons_size.y, s.y + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We also remove one row for the buttons.
|
|
||||||
let taken = nomans_land + Vec2::new(0, buttons_size.y);
|
|
||||||
|
|
||||||
let content_req = match req.checked_sub(taken) {
|
|
||||||
Some(r) => r,
|
|
||||||
// Bad!!
|
|
||||||
None => return taken,
|
|
||||||
};
|
|
||||||
|
|
||||||
let content_size = self.content.required_size(content_req);
|
|
||||||
|
|
||||||
// On the Y axis, we add buttons and content.
|
|
||||||
// On the X axis, we take the max.
|
|
||||||
let mut inner_size = Vec2::new(max(content_size.x, buttons_size.x),
|
|
||||||
content_size.y + buttons_size.y) +
|
|
||||||
self.padding.combined() +
|
|
||||||
self.borders.combined();
|
|
||||||
|
|
||||||
if !self.title.is_empty() {
|
|
||||||
// If we have a title, we have to fit it too!
|
|
||||||
inner_size.x = max(inner_size.x, self.title.width() + 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
inner_size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(&mut self, mut size: Vec2) {
|
|
||||||
// Padding and borders are taken, sorry.
|
|
||||||
// TODO: handle border-less themes?
|
|
||||||
let taken = self.borders.combined() + self.padding.combined();
|
|
||||||
size = size.saturating_sub(taken);
|
|
||||||
|
|
||||||
// Buttons are kings, we give them everything they want.
|
|
||||||
let mut buttons_height = 0;
|
|
||||||
for button in self.buttons.iter_mut().rev() {
|
|
||||||
let size = button.required_size(size);
|
|
||||||
buttons_height = max(buttons_height, size.y + 1);
|
|
||||||
button.layout(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Poor content will have to make do with what's left.
|
|
||||||
if buttons_height > size.y {
|
|
||||||
buttons_height = size.y;
|
|
||||||
}
|
|
||||||
self.content.layout(size.saturating_sub((0, buttons_height)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
|
||||||
match self.focus {
|
|
||||||
// If we are on the content, we can only go down.
|
|
||||||
Focus::Content => {
|
|
||||||
match self.content.on_event(event.clone()) {
|
|
||||||
EventResult::Ignored if !self.buttons.is_empty() => {
|
EventResult::Ignored if !self.buttons.is_empty() => {
|
||||||
match event {
|
match event {
|
||||||
Event::Key(Key::Down) |
|
Event::Key(Key::Down) |
|
||||||
@ -328,9 +221,18 @@ impl View for Dialog {
|
|||||||
res => res,
|
res => res,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If we are on a button, we have more choice
|
|
||||||
Focus::Button(i) => {
|
// An event is received while a button is in focus
|
||||||
match self.buttons[i].on_event(event.clone()) {
|
fn on_event_button(
|
||||||
|
&mut self, event: Event, button_id: usize
|
||||||
|
) -> EventResult {
|
||||||
|
let result = {
|
||||||
|
let button = &mut self.buttons[button_id];
|
||||||
|
button
|
||||||
|
.button
|
||||||
|
.on_event(event.relativized(button.offset.get()))
|
||||||
|
};
|
||||||
|
match result {
|
||||||
EventResult::Ignored => {
|
EventResult::Ignored => {
|
||||||
match event {
|
match event {
|
||||||
// Up goes back to the content
|
// Up goes back to the content
|
||||||
@ -351,8 +253,7 @@ impl View for Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Key(Key::Tab) => {
|
Event::Key(Key::Tab) => {
|
||||||
if self.content
|
if self.content.take_focus(Direction::front()) {
|
||||||
.take_focus(Direction::front()) {
|
|
||||||
self.focus = Focus::Content;
|
self.focus = Focus::Content;
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
} else {
|
} else {
|
||||||
@ -360,13 +261,14 @@ impl View for Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Left and Right move to other buttons
|
// Left and Right move to other buttons
|
||||||
Event::Key(Key::Right) if i + 1 <
|
Event::Key(Key::Right)
|
||||||
self.buttons.len() => {
|
if button_id + 1 < self.buttons.len() =>
|
||||||
self.focus = Focus::Button(i + 1);
|
{
|
||||||
|
self.focus = Focus::Button(button_id + 1);
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
Event::Key(Key::Left) if i > 0 => {
|
Event::Key(Key::Left) if button_id > 0 => {
|
||||||
self.focus = Focus::Button(i - 1);
|
self.focus = Focus::Button(button_id - 1);
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
_ => EventResult::Ignored,
|
_ => EventResult::Ignored,
|
||||||
@ -375,6 +277,213 @@ impl View for Dialog {
|
|||||||
res => res,
|
res => res,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw_buttons(&self, printer: &Printer) -> Option<usize> {
|
||||||
|
let mut buttons_height = 0;
|
||||||
|
// Current horizontal position of the next button we'll draw.
|
||||||
|
|
||||||
|
// Sum of the sizes + len-1 for margins
|
||||||
|
let width = self.buttons
|
||||||
|
.iter()
|
||||||
|
.map(|button| button.button.size.x)
|
||||||
|
.fold(0, |a, b| a + b)
|
||||||
|
+ self.buttons.len().saturating_sub(1);
|
||||||
|
let overhead = self.padding + self.borders;
|
||||||
|
if printer.size.x < overhead.horizontal() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut offset = overhead.left
|
||||||
|
+ self.align
|
||||||
|
.h
|
||||||
|
.get_offset(width, printer.size.x - overhead.horizontal());
|
||||||
|
|
||||||
|
let overhead_bottom = self.padding.bottom + self.borders.bottom + 1;
|
||||||
|
|
||||||
|
let y = match printer.size.y.checked_sub(overhead_bottom) {
|
||||||
|
Some(y) => y,
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i, button) in self.buttons.iter().enumerate() {
|
||||||
|
let size = button.button.size;
|
||||||
|
// Add some special effect to the focused button
|
||||||
|
let position = Vec2::new(offset, y);
|
||||||
|
button.offset.set(position);
|
||||||
|
button.button.draw(&printer.sub_printer(
|
||||||
|
position,
|
||||||
|
size,
|
||||||
|
self.focus == Focus::Button(i),
|
||||||
|
));
|
||||||
|
// Keep 1 blank between two buttons
|
||||||
|
offset += size.x + 1;
|
||||||
|
// Also keep 1 blank above the buttons
|
||||||
|
buttons_height = max(buttons_height, size.y + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(buttons_height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_content(&self, printer: &Printer, buttons_height: usize) {
|
||||||
|
// What do we have left?
|
||||||
|
let taken = Vec2::new(0, buttons_height) + self.borders.combined()
|
||||||
|
+ self.padding.combined();
|
||||||
|
|
||||||
|
let inner_size = match printer.size.checked_sub(taken) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.content.draw(&printer.sub_printer(
|
||||||
|
self.borders.top_left() + self.padding.top_left(),
|
||||||
|
inner_size,
|
||||||
|
self.focus == Focus::Content,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_title(&self, printer: &Printer) {
|
||||||
|
if !self.title.is_empty() {
|
||||||
|
let len = self.title.width();
|
||||||
|
if len + 4 > printer.size.x {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let x = (printer.size.x - len) / 2;
|
||||||
|
printer.with_high_border(false, |printer| {
|
||||||
|
printer.print((x - 2, 0), "┤ ");
|
||||||
|
printer.print((x + len, 0), " ├");
|
||||||
|
});
|
||||||
|
|
||||||
|
printer.with_color(
|
||||||
|
ColorStyle::TitlePrimary,
|
||||||
|
|p| p.print((x, 0), &self.title),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_focus_grab(&mut self, event: &Event) {
|
||||||
|
if let &Event::Mouse {
|
||||||
|
offset,
|
||||||
|
position,
|
||||||
|
event,
|
||||||
|
} = event
|
||||||
|
{
|
||||||
|
if !event.grabs_focus() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let position = match position.checked_sub(offset) {
|
||||||
|
None => return,
|
||||||
|
Some(pos) => pos,
|
||||||
|
};
|
||||||
|
|
||||||
|
eprintln!("Rel pos: {:?}", position);
|
||||||
|
|
||||||
|
// Now that we have a relative position, checks for buttons?
|
||||||
|
if let Some(i) = self.buttons.iter().position(|btn| {
|
||||||
|
// If position fits there...
|
||||||
|
position.fits_in_rect(btn.offset.get(), btn.button.size)
|
||||||
|
}) {
|
||||||
|
self.focus = Focus::Button(i);
|
||||||
|
} else if position.fits_in_rect((1, 1), self.content.size)
|
||||||
|
&& self.content.take_focus(Direction::none())
|
||||||
|
{
|
||||||
|
// Or did we click the content?
|
||||||
|
self.focus = Focus::Content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for Dialog {
|
||||||
|
fn draw(&self, printer: &Printer) {
|
||||||
|
// This will be the buttons_height used by the buttons.
|
||||||
|
let buttons_height = match self.draw_buttons(printer) {
|
||||||
|
Some(height) => height,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.draw_content(printer, buttons_height);
|
||||||
|
|
||||||
|
// Print the borders
|
||||||
|
printer.print_box(Vec2::new(0, 0), printer.size, false);
|
||||||
|
|
||||||
|
self.draw_title(printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
||||||
|
// Padding and borders are not available for kids.
|
||||||
|
let nomans_land = self.padding.combined() + self.borders.combined();
|
||||||
|
|
||||||
|
// Buttons are not flexible, so their size doesn't depend on ours.
|
||||||
|
let mut buttons_size = Vec2::new(0, 0);
|
||||||
|
|
||||||
|
// Start with the inter-button space.
|
||||||
|
buttons_size.x += self.buttons.len().saturating_sub(1);
|
||||||
|
|
||||||
|
for button in &mut self.buttons {
|
||||||
|
let s = button.button.view.required_size(req);
|
||||||
|
buttons_size.x += s.x;
|
||||||
|
buttons_size.y = max(buttons_size.y, s.y + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We also remove one row for the buttons.
|
||||||
|
let taken = nomans_land + Vec2::new(0, buttons_size.y);
|
||||||
|
|
||||||
|
let content_req = match req.checked_sub(taken) {
|
||||||
|
Some(r) => r,
|
||||||
|
// Bad!!
|
||||||
|
None => return taken,
|
||||||
|
};
|
||||||
|
|
||||||
|
let content_size = self.content.required_size(content_req);
|
||||||
|
|
||||||
|
// On the Y axis, we add buttons and content.
|
||||||
|
// On the X axis, we take the max.
|
||||||
|
let mut inner_size = Vec2::new(
|
||||||
|
max(content_size.x, buttons_size.x),
|
||||||
|
content_size.y + buttons_size.y,
|
||||||
|
) + self.padding.combined()
|
||||||
|
+ self.borders.combined();
|
||||||
|
|
||||||
|
if !self.title.is_empty() {
|
||||||
|
// If we have a title, we have to fit it too!
|
||||||
|
inner_size.x = max(inner_size.x, self.title.width() + 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(&mut self, mut size: Vec2) {
|
||||||
|
// Padding and borders are taken, sorry.
|
||||||
|
// TODO: handle border-less themes?
|
||||||
|
let taken = self.borders.combined() + self.padding.combined();
|
||||||
|
size = size.saturating_sub(taken);
|
||||||
|
|
||||||
|
// Buttons are kings, we give them everything they want.
|
||||||
|
let mut buttons_height = 0;
|
||||||
|
for button in self.buttons.iter_mut().rev() {
|
||||||
|
let size = button.button.required_size(size);
|
||||||
|
buttons_height = max(buttons_height, size.y + 1);
|
||||||
|
button.button.layout(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poor content will have to make do with what's left.
|
||||||
|
if buttons_height > size.y {
|
||||||
|
buttons_height = size.y;
|
||||||
|
}
|
||||||
|
self.content
|
||||||
|
.layout(size.saturating_sub((0, buttons_height)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
|
// First: some mouse events can instantly change the focus.
|
||||||
|
self.check_focus_grab(&event);
|
||||||
|
|
||||||
|
match self.focus {
|
||||||
|
// If we are on the content, we can only go down.
|
||||||
|
// TODO: Careful if/when we add buttons elsewhere on the dialog!
|
||||||
|
Focus::Content => self.on_event_content(event),
|
||||||
|
// If we are on a button, we have more choice
|
||||||
|
Focus::Button(i) => self.on_event_button(event, i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,8 +501,9 @@ impl View for Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_on_any<'a>(&mut self, selector: &Selector,
|
fn call_on_any<'a>(
|
||||||
callback: Box<FnMut(&mut Any) + 'a>) {
|
&mut self, selector: &Selector, callback: Box<FnMut(&mut Any) + 'a>
|
||||||
|
) {
|
||||||
self.content.call_on_any(selector, callback);
|
self.content.call_on_any(selector, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user