From c76b6900dadf163f51553789b2665ff756312a78 Mon Sep 17 00:00:00 2001 From: "razvan.iliescu2912" Date: Sun, 10 Nov 2024 16:53:16 +0200 Subject: [PATCH 1/9] Added option for vertical orientation of buttons for dialog box --- cursive-core/src/views/dialog.rs | 202 ++++++++++++++++++++++--------- cursive/examples/dialog.rs | 7 +- 2 files changed, 146 insertions(+), 63 deletions(-) diff --git a/cursive-core/src/views/dialog.rs b/cursive-core/src/views/dialog.rs index 427ac42f..d9f02901 100644 --- a/cursive-core/src/views/dialog.rs +++ b/cursive-core/src/views/dialog.rs @@ -1,13 +1,5 @@ use crate::{ - align::*, - direction::{Absolute, Direction, Relative}, - event::{AnyCb, Event, EventResult, Key}, - rect::Rect, - style::PaletteStyle, - utils::markup::StyledString, - view::{CannotFocus, IntoBoxedView, Margins, Selector, View, ViewNotFound}, - views::{BoxedView, Button, DummyView, LastSizeView, TextView}, - Cursive, Printer, Vec2, With, + align::*, direction::{Absolute, Direction, Relative}, event::{AnyCb, Event, EventResult, Key}, rect::Rect, style::PaletteStyle, utils::markup::StyledString, view::{CannotFocus, IntoBoxedView, Margins, Selector, View, ViewNotFound}, views::{BoxedView, Button, DummyView, LastSizeView, TextView}, Cursive, Printer, Vec2, With }; use parking_lot::Mutex; use std::cmp::{max, min}; @@ -101,6 +93,9 @@ pub struct Dialog { // Include the top-left corner. buttons: Vec, + // Option to set the buttons as a list next to the dialog box + vertical_button_orientation: bool, + // Padding around the inner view. padding: Margins, @@ -132,6 +127,7 @@ impl Dialog { Dialog { content: LastSizeView::new(BoxedView::boxed(view)), buttons: Vec::new(), + vertical_button_orientation: false, title: StyledString::new(), title_position: HAlign::Center, focus: DialogFocus::Content, @@ -162,6 +158,16 @@ impl Dialog { }) } + /// Changes the button layout orientation + /// + /// The default orientation is horizontal + pub fn set_button_orientation(mut self, v: bool) -> Self { + self.vertical_button_orientation = v; + + self + } + + /// Gets the content of this dialog. /// /// ``` @@ -318,9 +324,6 @@ impl Dialog { self.align.h } - /* - * Commented out because currently un-implemented. - * /// Sets the vertical alignment for the buttons, if any. /// /// Only works if the buttons are as a column to the right of the dialog. @@ -330,7 +333,14 @@ impl Dialog { self } - */ + + /// Gets the vertical alignment of the buttons, if any. + /// + /// Only works if the buttons are as a column to the right of the dialog. + pub fn get_v_align(&self) -> VAlign { + self.align.v + } + /// Shortcut method to add a button that will dismiss the dialog. /// @@ -556,7 +566,7 @@ impl Dialog { EventResult::Ignored => { match event { // Up goes back to the content - Event::Key(Key::Up) => { + Event::Key(Key::Up) if self.vertical_button_orientation == false => { if let Ok(res) = self.content.take_focus(Direction::down()) { self.focus = DialogFocus::Content; res @@ -564,6 +574,16 @@ impl Dialog { EventResult::Ignored } } + + Event::Key(Key::Left) if self.vertical_button_orientation == true => { + if let Ok(res) = self.content.take_focus(Direction::down()) { + self.focus = DialogFocus::Content; + res + } else { + EventResult::Ignored + } + } + Event::Shift(Key::Tab) if self.focus == DialogFocus::Button(0) => { // If we're at the first button, jump back to the content. if let Ok(res) = self.content.take_focus(Direction::back()) { @@ -596,12 +616,22 @@ impl Dialog { } EventResult::Consumed(None) } - // Left and Right move to other buttons - Event::Key(Key::Right) if button_id + 1 < self.buttons.len() => { + + Event::Key(Key::Right) if self.vertical_button_orientation == false && button_id + 1 < self.buttons.len() => { self.focus = DialogFocus::Button(button_id + 1); EventResult::Consumed(None) } - Event::Key(Key::Left) if button_id > 0 => { + Event::Key(Key::Left) if self.vertical_button_orientation == false && button_id > 0 => { + self.focus = DialogFocus::Button(button_id - 1); + EventResult::Consumed(None) + } + + Event::Key(Key::Down) if self.vertical_button_orientation == true && button_id + 1 < self.buttons.len() => { + self.focus = DialogFocus::Button(button_id + 1); + EventResult::Consumed(None) + } + + Event::Key(Key::Up) if self.vertical_button_orientation == true && button_id > 0 => { self.focus = DialogFocus::Button(button_id - 1); EventResult::Consumed(None) } @@ -613,51 +643,103 @@ impl Dialog { } fn draw_buttons(&self, printer: &Printer) -> Option { - 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) - .sum::() - + 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, - }; + match self.vertical_button_orientation { + false => { + 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) + .sum::() + + 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.lock() = position; + button.button.draw( + &printer + .offset(position) + .cropped(size) + .focused(self.focus == DialogFocus::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); + } + + return Some(buttons_height); + } + true => { + let mut buttons_width = 0; + + // Calculate the total height of buttons including spacing + let height = self + .buttons + .iter() + .map(|button| button.button.size.y) + .sum::() + + self.buttons.len().saturating_sub(1); + + + let overhead = self.padding + self.borders; + if printer.size.y < overhead.vertical() { + return None; + } + let mut offset = overhead.top + + self + .align + .v + .get_offset(height, printer.size.y - overhead.vertical()); + + let overhead_right = self.padding.right + self.borders.right + 1; + + let x = match printer.size.x.checked_sub(overhead_right) { + Some(x) => x, + 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(x, offset); + *button.offset.lock() = position; + button.button.draw( + &printer + .offset(position) + .cropped(size) + .focused(self.focus == DialogFocus::Button(i)), + ); + // Keep 1 blank between two buttons + offset += size.y + 1; + // Also keep 1 blank to the left of the buttons + buttons_width = max(buttons_width, size.x + 1); + } - 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.lock() = position; - button.button.draw( - &printer - .offset(position) - .cropped(size) - .focused(self.focus == DialogFocus::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); + return Some(buttons_width); + } } - - Some(buttons_height) } fn draw_content(&self, printer: &Printer, buttons_height: usize) { diff --git a/cursive/examples/dialog.rs b/cursive/examples/dialog.rs index c044205e..1ac86083 100644 --- a/cursive/examples/dialog.rs +++ b/cursive/examples/dialog.rs @@ -1,6 +1,5 @@ use cursive::{ - views::{CircularFocus, Dialog, TextView}, - With as _, + views::{CircularFocus, Dialog, TextView}, With as _ }; fn main() { @@ -11,11 +10,13 @@ fn main() { siv.add_layer( // Most views can be configured in a chainable way Dialog::around(TextView::new("Hello Dialog!")) + .set_button_orientation(true) .title("Cursive") .button("Foo", |_s| ()) + .button("Bar", |_s| ()) .button("Quit", |s| s.quit()) .wrap_with(CircularFocus::new) - .wrap_tab(), + .wrap_tab() ); // Starts the event loop. From 3469aa6fdaf36c39b3f10dbbe1e7afaf785ee528 Mon Sep 17 00:00:00 2001 From: "razvan.iliescu2912" Date: Sun, 10 Nov 2024 17:50:06 +0200 Subject: [PATCH 2/9] Fixed the implementation (the buttons are fully shown) --- cursive-core/src/views/dialog.rs | 278 ++++++++++++++++++++++--------- 1 file changed, 203 insertions(+), 75 deletions(-) diff --git a/cursive-core/src/views/dialog.rs b/cursive-core/src/views/dialog.rs index d9f02901..3710f600 100644 --- a/cursive-core/src/views/dialog.rs +++ b/cursive-core/src/views/dialog.rs @@ -565,7 +565,7 @@ impl Dialog { match result { EventResult::Ignored => { match event { - // Up goes back to the content + // Up goes back to the content if buttons are horizontal Event::Key(Key::Up) if self.vertical_button_orientation == false => { if let Ok(res) = self.content.take_focus(Direction::down()) { self.focus = DialogFocus::Content; @@ -575,6 +575,7 @@ impl Dialog { } } + // Left goes back to the content if buttons are vertical Event::Key(Key::Left) if self.vertical_button_orientation == true => { if let Ok(res) = self.content.take_focus(Direction::down()) { self.focus = DialogFocus::Content; @@ -647,7 +648,7 @@ impl Dialog { false => { 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 @@ -692,6 +693,7 @@ impl Dialog { return Some(buttons_height); } true => { + // Current horizontal position of the next button let mut buttons_width = 0; // Calculate the total height of buttons including spacing @@ -702,9 +704,9 @@ impl Dialog { .sum::() + self.buttons.len().saturating_sub(1); - + let max_size = self.buttons.iter().map(|button|button.button.size.x).max().unwrap_or_default(); let overhead = self.padding + self.borders; - if printer.size.y < overhead.vertical() { + if printer.size.y < overhead.horizontal() { return None; } let mut offset = overhead.top @@ -713,7 +715,7 @@ impl Dialog { .v .get_offset(height, printer.size.y - overhead.vertical()); - let overhead_right = self.padding.right + self.borders.right + 1; + let overhead_right = self.padding.right + self.borders.right + max_size; let x = match printer.size.x.checked_sub(overhead_right) { Some(x) => x, @@ -742,22 +744,48 @@ impl Dialog { } } - fn draw_content(&self, printer: &Printer, buttons_height: usize) { + fn draw_content(&self, printer: &Printer, buttons_size: usize) { // What do we have left? - let taken = - Vec2::new(0, buttons_height) + self.borders.combined() + self.padding.combined(); + match self.vertical_button_orientation { + false => { + let taken = + Vec2::new(0, buttons_size) + self.borders.combined() + self.padding.combined(); - let inner_size = match printer.size.checked_sub(taken) { - Some(s) => s, - None => return, - }; + let inner_size = match printer.size.checked_sub(taken) { + Some(s) => s, + None => return, + }; + + self.content.draw( + &printer + .offset(self.borders.top_left() + self.padding.top_left()) + .cropped(inner_size) + .focused(self.focus == DialogFocus::Content), + ); + } + true => { + // TODO: show content if the buttons are vertically aligned + let taken = Vec2::new(buttons_size, 0) + self.padding.combined(); - self.content.draw( - &printer - .offset(self.borders.top_left() + self.padding.top_left()) - .cropped(inner_size) - .focused(self.focus == DialogFocus::Content), - ); + // Determine the remaining size for content + let inner_size = match printer.size.checked_sub(taken + self.borders.combined()) { + Some(s) => s, + None => return, // Not enough space for content + }; + + // Offset the content to leave space for the button column + self.content.draw( + &printer + .offset( + self.borders.top_left() + + self.padding.top_left(), // Padding for the content + ) + .cropped(inner_size) + .focused(self.focus == DialogFocus::Content), + ); + } + } + } fn draw_title(&self, printer: &Printer) { @@ -827,86 +855,186 @@ impl Dialog { 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, - }; + match self.vertical_button_orientation { + false => { + // 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); + self.draw_content(printer, buttons_height); - // Print the borders - printer.print_box(Vec2::new(0, 0), printer.size, false); + // Print the borders + printer.print_box(Vec2::new(0, 0), printer.size, false); - self.draw_title(printer); + self.draw_title(printer); + } + true => { + let buttons_width = match self.draw_buttons(printer) { + Some(width) => width, + None => return, + }; + + // Adjust the content area size + let content_area_width = printer.size.x.saturating_sub(buttons_width + self.borders.horizontal()); + let content_area_height = printer.size.y.saturating_sub(self.borders.vertical()); + + // Check if there's enough space for the content + if content_area_width == 0 || content_area_height == 0 { + return; // Not enough space for content + } + + // Define the printer for the content area + let content_printer = printer + .offset(Vec2::new(self.borders.left, self.borders.top)) // Adjust for borders + .cropped(Vec2::new(content_area_width, content_area_height)); + + // Draw the content + self.content.draw(&content_printer); + + // Print the borders + printer.print_box(Vec2::new(0, 0), printer.size, false); + + // Draw the title + 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(); + match self.vertical_button_orientation { + false => { + // 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); + // 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); + // 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); - } + 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); + // 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_req = match req.checked_sub(taken) { + Some(r) => r, + // Bad!! + None => return taken, + }; - let content_size = self.content.required_size(content_req); + 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(); + // 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); - } + 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 + } + true => { + // 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 (vertical spacing). + buttons_size.y += self.buttons.len().saturating_sub(1); + + for button in &mut self.buttons { + let s = button.button.view.required_size(req); + buttons_size.y += s.y; + buttons_size.x = max(buttons_size.x, s.x + 1); // Track max width + } - inner_size + // Remove the width taken by the buttons from the available space for content + let taken = nomans_land + Vec2::new(buttons_size.x, 0); + + let content_req = match req.checked_sub(taken) { + Some(r) => r, + // If there's no space for content, return the size taken by buttons + None => return taken, + }; + + let content_size = self.content.required_size(content_req); + + // On the Y axis, we take the max of content and buttons height. + // On the X axis, we add the content and buttons widths together. + let mut inner_size = Vec2::new( + content_size.x + buttons_size.x, // Add button width to content width + max(content_size.y, buttons_size.y), // Max height of content and buttons + ) + self.padding.combined() + + self.borders.combined(); + + if !self.title.is_empty() { + // If we have a title, make sure it fits within the dialog width. + 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); - } + match self.vertical_button_orientation { + false => { + let taken = self.borders.combined() + self.padding.combined(); + size = size.saturating_sub(taken); - // Poor content will have to make do with what's left. - if buttons_height > size.y { - buttons_height = size.y; - } + // 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))); + self.content + .layout(size.saturating_sub((0, buttons_height))); - self.invalidated = false; + self.invalidated = false; + } + true => { + //TODO: set layout if buttons are oriented vertically + let mut buttons_width = 0; + for button in &mut self.buttons { + let button_size = button.button.required_size(size); + buttons_width = max(buttons_width, button_size.x + 1); + button.button.layout(button_size); + } + + // Calculate the size left for the content + let content_size = size.saturating_sub((buttons_width, 0)); + + // Poor content will have to fit into the remaining space. + self.content.layout(content_size); + + self.invalidated = false; + } + } } fn on_event(&mut self, event: Event) -> EventResult { From 259e909754553ec3a39fe6fa4ea116cef84928a3 Mon Sep 17 00:00:00 2001 From: "razvan.iliescu2912" Date: Fri, 15 Nov 2024 11:31:52 +0200 Subject: [PATCH 3/9] Changed the field type from bool to direction::Orientation --- cursive-core/src/views/dialog.rs | 55 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/cursive-core/src/views/dialog.rs b/cursive-core/src/views/dialog.rs index 3710f600..1add836f 100644 --- a/cursive-core/src/views/dialog.rs +++ b/cursive-core/src/views/dialog.rs @@ -1,5 +1,5 @@ use crate::{ - align::*, direction::{Absolute, Direction, Relative}, event::{AnyCb, Event, EventResult, Key}, rect::Rect, style::PaletteStyle, utils::markup::StyledString, view::{CannotFocus, IntoBoxedView, Margins, Selector, View, ViewNotFound}, views::{BoxedView, Button, DummyView, LastSizeView, TextView}, Cursive, Printer, Vec2, With + align::*, direction::{self, Absolute, Direction, Relative}, event::{AnyCb, Event, EventResult, Key}, rect::Rect, style::PaletteStyle, utils::markup::StyledString, view::{CannotFocus, IntoBoxedView, Margins, Selector, View, ViewNotFound}, views::{BoxedView, Button, DummyView, LastSizeView, TextView}, Cursive, Printer, Vec2, With }; use parking_lot::Mutex; use std::cmp::{max, min}; @@ -94,7 +94,7 @@ pub struct Dialog { buttons: Vec, // Option to set the buttons as a list next to the dialog box - vertical_button_orientation: bool, + button_orientation: direction::Orientation, // Padding around the inner view. padding: Margins, @@ -127,7 +127,7 @@ impl Dialog { Dialog { content: LastSizeView::new(BoxedView::boxed(view)), buttons: Vec::new(), - vertical_button_orientation: false, + button_orientation: direction::Orientation::Horizontal, title: StyledString::new(), title_position: HAlign::Center, focus: DialogFocus::Content, @@ -161,8 +161,8 @@ impl Dialog { /// Changes the button layout orientation /// /// The default orientation is horizontal - pub fn set_button_orientation(mut self, v: bool) -> Self { - self.vertical_button_orientation = v; + pub fn set_button_orientation(mut self, v: direction::Orientation) -> Self { + self.button_orientation = v; self } @@ -566,7 +566,7 @@ impl Dialog { EventResult::Ignored => { match event { // Up goes back to the content if buttons are horizontal - Event::Key(Key::Up) if self.vertical_button_orientation == false => { + Event::Key(Key::Up) if self.button_orientation == direction::Orientation::Horizontal => { if let Ok(res) = self.content.take_focus(Direction::down()) { self.focus = DialogFocus::Content; res @@ -576,8 +576,8 @@ impl Dialog { } // Left goes back to the content if buttons are vertical - Event::Key(Key::Left) if self.vertical_button_orientation == true => { - if let Ok(res) = self.content.take_focus(Direction::down()) { + Event::Key(Key::Left) if self.button_orientation == direction::Orientation::Vertical => { + if let Ok(res) = self.content.take_focus(Direction::right()) { self.focus = DialogFocus::Content; res } else { @@ -618,21 +618,21 @@ impl Dialog { EventResult::Consumed(None) } - Event::Key(Key::Right) if self.vertical_button_orientation == false && button_id + 1 < self.buttons.len() => { + Event::Key(Key::Right) if self.button_orientation == direction::Orientation::Horizontal && button_id + 1 < self.buttons.len() => { self.focus = DialogFocus::Button(button_id + 1); EventResult::Consumed(None) } - Event::Key(Key::Left) if self.vertical_button_orientation == false && button_id > 0 => { + Event::Key(Key::Left) if self.button_orientation == direction::Orientation::Horizontal && button_id > 0 => { self.focus = DialogFocus::Button(button_id - 1); EventResult::Consumed(None) } - Event::Key(Key::Down) if self.vertical_button_orientation == true && button_id + 1 < self.buttons.len() => { + Event::Key(Key::Down) if self.button_orientation == direction::Orientation::Vertical && button_id + 1 < self.buttons.len() => { self.focus = DialogFocus::Button(button_id + 1); EventResult::Consumed(None) } - Event::Key(Key::Up) if self.vertical_button_orientation == true && button_id > 0 => { + Event::Key(Key::Up) if self.button_orientation == direction::Orientation::Vertical && button_id > 0 => { self.focus = DialogFocus::Button(button_id - 1); EventResult::Consumed(None) } @@ -644,8 +644,8 @@ impl Dialog { } fn draw_buttons(&self, printer: &Printer) -> Option { - match self.vertical_button_orientation { - false => { + match self.button_orientation { + direction::Orientation::Horizontal => { let mut buttons_height = 0; // Current horizontal position of the next button we'll draw. @@ -692,7 +692,7 @@ impl Dialog { return Some(buttons_height); } - true => { + direction::Orientation::Vertical => { // Current horizontal position of the next button let mut buttons_width = 0; @@ -746,8 +746,8 @@ impl Dialog { fn draw_content(&self, printer: &Printer, buttons_size: usize) { // What do we have left? - match self.vertical_button_orientation { - false => { + match self.button_orientation { + direction::Orientation::Horizontal => { let taken = Vec2::new(0, buttons_size) + self.borders.combined() + self.padding.combined(); @@ -763,7 +763,7 @@ impl Dialog { .focused(self.focus == DialogFocus::Content), ); } - true => { + direction::Orientation::Vertical => { // TODO: show content if the buttons are vertically aligned let taken = Vec2::new(buttons_size, 0) + self.padding.combined(); @@ -855,8 +855,8 @@ impl Dialog { impl View for Dialog { fn draw(&self, printer: &Printer) { - match self.vertical_button_orientation { - false => { + match self.button_orientation { + direction::Orientation::Horizontal => { // This will be the buttons_height used by the buttons. let buttons_height = match self.draw_buttons(printer) { Some(height) => height, @@ -870,7 +870,7 @@ impl View for Dialog { self.draw_title(printer); } - true => { + direction::Orientation::Vertical => { let buttons_width = match self.draw_buttons(printer) { Some(width) => width, None => return, @@ -903,8 +903,8 @@ impl View for Dialog { } fn required_size(&mut self, req: Vec2) -> Vec2 { - match self.vertical_button_orientation { - false => { + match self.button_orientation { + direction::Orientation::Horizontal => { // Padding and borders are not available for kids. let nomans_land = self.padding.combined() + self.borders.combined(); @@ -946,7 +946,7 @@ impl View for Dialog { inner_size } - true => { + direction::Orientation::Vertical => { // Padding and borders are not available for kids. let nomans_land = self.padding.combined() + self.borders.combined(); @@ -994,8 +994,8 @@ impl View for Dialog { fn layout(&mut self, mut size: Vec2) { // Padding and borders are taken, sorry. // TODO: handle border-less themes? - match self.vertical_button_orientation { - false => { + match self.button_orientation { + direction::Orientation::Horizontal => { let taken = self.borders.combined() + self.padding.combined(); size = size.saturating_sub(taken); @@ -1017,8 +1017,7 @@ impl View for Dialog { self.invalidated = false; } - true => { - //TODO: set layout if buttons are oriented vertically + direction::Orientation::Vertical => { let mut buttons_width = 0; for button in &mut self.buttons { let button_size = button.button.required_size(size); From 93070accf2681cd0cf29e536bb3abd58bd616dce Mon Sep 17 00:00:00 2001 From: "razvan.iliescu2912" Date: Fri, 15 Nov 2024 11:33:32 +0200 Subject: [PATCH 4/9] Changed the example accordingly --- cursive/examples/dialog.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cursive/examples/dialog.rs b/cursive/examples/dialog.rs index 1ac86083..4461771b 100644 --- a/cursive/examples/dialog.rs +++ b/cursive/examples/dialog.rs @@ -1,5 +1,5 @@ use cursive::{ - views::{CircularFocus, Dialog, TextView}, With as _ + direction, views::{CircularFocus, Dialog, TextView}, With as _ }; fn main() { @@ -10,7 +10,7 @@ fn main() { siv.add_layer( // Most views can be configured in a chainable way Dialog::around(TextView::new("Hello Dialog!")) - .set_button_orientation(true) + .set_button_orientation(direction::Orientation::Vertical) .title("Cursive") .button("Foo", |_s| ()) .button("Bar", |_s| ()) From 1212be98be3fe2828aa47d8c2e62c45b1c4d78d4 Mon Sep 17 00:00:00 2001 From: "razvan.iliescu2912" Date: Fri, 15 Nov 2024 18:10:01 +0200 Subject: [PATCH 5/9] Changed the field type from bool to direction::Orientation --- cursive-core/src/views/dialog.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cursive-core/src/views/dialog.rs b/cursive-core/src/views/dialog.rs index 1add836f..da62067b 100644 --- a/cursive-core/src/views/dialog.rs +++ b/cursive-core/src/views/dialog.rs @@ -93,7 +93,7 @@ pub struct Dialog { // Include the top-left corner. buttons: Vec, - // Option to set the buttons as a list next to the dialog box + // Option to change the orientation of the buttons in the dialog box button_orientation: direction::Orientation, // Padding around the inner view. From 8b83360fd85d06c3ebb1604fb4bb0d8e87a9e269 Mon Sep 17 00:00:00 2001 From: "razvan.iliescu2912" Date: Fri, 15 Nov 2024 18:35:45 +0200 Subject: [PATCH 6/9] Changed the on_event_button switch to match on (event, self.button_orientation) --- cursive-core/src/views/dialog.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cursive-core/src/views/dialog.rs b/cursive-core/src/views/dialog.rs index da62067b..53c9dbb0 100644 --- a/cursive-core/src/views/dialog.rs +++ b/cursive-core/src/views/dialog.rs @@ -564,9 +564,9 @@ impl Dialog { }; match result { EventResult::Ignored => { - match event { + match (event, self.button_orientation) { // Up goes back to the content if buttons are horizontal - Event::Key(Key::Up) if self.button_orientation == direction::Orientation::Horizontal => { + (Event::Key(Key::Up), direction::Orientation::Horizontal) => { if let Ok(res) = self.content.take_focus(Direction::down()) { self.focus = DialogFocus::Content; res @@ -576,7 +576,7 @@ impl Dialog { } // Left goes back to the content if buttons are vertical - Event::Key(Key::Left) if self.button_orientation == direction::Orientation::Vertical => { + (Event::Key(Key::Left), direction::Orientation::Vertical) => { if let Ok(res) = self.content.take_focus(Direction::right()) { self.focus = DialogFocus::Content; res @@ -585,7 +585,7 @@ impl Dialog { } } - Event::Shift(Key::Tab) if self.focus == DialogFocus::Button(0) => { + (Event::Shift(Key::Tab), _) if self.focus == DialogFocus::Button(0) => { // If we're at the first button, jump back to the content. if let Ok(res) = self.content.take_focus(Direction::back()) { self.focus = DialogFocus::Content; @@ -594,7 +594,7 @@ impl Dialog { EventResult::Ignored } } - Event::Shift(Key::Tab) => { + (Event::Shift(Key::Tab), _) => { // Otherwise, jump to the previous button. if let DialogFocus::Button(ref mut i) = self.focus { // This should always be the case. @@ -602,14 +602,14 @@ impl Dialog { } EventResult::Consumed(None) } - Event::Key(Key::Tab) + (Event::Key(Key::Tab), _) if self.focus == DialogFocus::Button(self.buttons.len().saturating_sub(1)) => { // End of the line EventResult::Ignored } - Event::Key(Key::Tab) => { + (Event::Key(Key::Tab), _) => { // Otherwise, jump to the next button. if let DialogFocus::Button(ref mut i) = self.focus { // This should always be the case. @@ -618,21 +618,21 @@ impl Dialog { EventResult::Consumed(None) } - Event::Key(Key::Right) if self.button_orientation == direction::Orientation::Horizontal && button_id + 1 < self.buttons.len() => { + (Event::Key(Key::Right), direction::Orientation::Horizontal) if button_id + 1 < self.buttons.len() => { self.focus = DialogFocus::Button(button_id + 1); EventResult::Consumed(None) } - Event::Key(Key::Left) if self.button_orientation == direction::Orientation::Horizontal && button_id > 0 => { + (Event::Key(Key::Left), direction::Orientation::Horizontal) if button_id > 0 => { self.focus = DialogFocus::Button(button_id - 1); EventResult::Consumed(None) } - Event::Key(Key::Down) if self.button_orientation == direction::Orientation::Vertical && button_id + 1 < self.buttons.len() => { + (Event::Key(Key::Down), direction::Orientation::Vertical) if button_id + 1 < self.buttons.len() => { self.focus = DialogFocus::Button(button_id + 1); EventResult::Consumed(None) } - Event::Key(Key::Up) if self.button_orientation == direction::Orientation::Vertical && button_id > 0 => { + (Event::Key(Key::Up), direction::Orientation::Vertical) if button_id > 0 => { self.focus = DialogFocus::Button(button_id - 1); EventResult::Consumed(None) } From 7af3208638ab40aa687461cfdbcd07416e1de440 Mon Sep 17 00:00:00 2001 From: "razvan.iliescu2912" Date: Fri, 15 Nov 2024 23:09:55 +0200 Subject: [PATCH 7/9] The draw content implementation now has a single branch --- cursive-core/src/views/dialog.rs | 52 ++++++++------------------------ 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/cursive-core/src/views/dialog.rs b/cursive-core/src/views/dialog.rs index 53c9dbb0..f05159ae 100644 --- a/cursive-core/src/views/dialog.rs +++ b/cursive-core/src/views/dialog.rs @@ -644,6 +644,7 @@ impl Dialog { } fn draw_buttons(&self, printer: &Printer) -> Option { + match self.button_orientation { direction::Orientation::Horizontal => { let mut buttons_height = 0; @@ -746,46 +747,19 @@ impl Dialog { fn draw_content(&self, printer: &Printer, buttons_size: usize) { // What do we have left? - match self.button_orientation { - direction::Orientation::Horizontal => { - let taken = - Vec2::new(0, buttons_size) + self.borders.combined() + self.padding.combined(); - - let inner_size = match printer.size.checked_sub(taken) { - Some(s) => s, - None => return, - }; - - self.content.draw( - &printer - .offset(self.borders.top_left() + self.padding.top_left()) - .cropped(inner_size) - .focused(self.focus == DialogFocus::Content), - ); - } - direction::Orientation::Vertical => { - // TODO: show content if the buttons are vertically aligned - let taken = Vec2::new(buttons_size, 0) + self.padding.combined(); - - // Determine the remaining size for content - let inner_size = match printer.size.checked_sub(taken + self.borders.combined()) { - Some(s) => s, - None => return, // Not enough space for content - }; + let buttons_size = Vec2::from_major_minor(self.button_orientation, buttons_size, 0); + let taken = buttons_size + self.borders.combined() + self.padding.combined(); + let inner_size = match printer.size.checked_sub(taken) { + Some(s) => s, + None => return, + }; - // Offset the content to leave space for the button column - self.content.draw( - &printer - .offset( - self.borders.top_left() - + self.padding.top_left(), // Padding for the content - ) - .cropped(inner_size) - .focused(self.focus == DialogFocus::Content), - ); - } - } - + self.content.draw( + &printer + .offset(self.borders.top_left() + self.padding.top_left()) + .cropped(inner_size) + .focused(self.focus == DialogFocus::Content), + ) } fn draw_title(&self, printer: &Printer) { From 7c87a80b6fc7096e06d19700e023e492e0d969c3 Mon Sep 17 00:00:00 2001 From: "razvan.iliescu2912" Date: Sun, 17 Nov 2024 12:49:30 +0200 Subject: [PATCH 8/9] The draw_buttons implementation now has a single branch --- cursive-core/src/views/dialog.rs | 138 +++++++++++++------------------ 1 file changed, 59 insertions(+), 79 deletions(-) diff --git a/cursive-core/src/views/dialog.rs b/cursive-core/src/views/dialog.rs index f05159ae..8af160c9 100644 --- a/cursive-core/src/views/dialog.rs +++ b/cursive-core/src/views/dialog.rs @@ -644,105 +644,85 @@ impl Dialog { } fn draw_buttons(&self, printer: &Printer) -> Option { - + let mut buttons_height: usize = 0; + let mut buttons_width: usize = 0; + let mut max_size = 1; match self.button_orientation { direction::Orientation::Horizontal => { - 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_width = self .buttons .iter() .map(|button| button.button.size.x) .sum::() + 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.lock() = position; - button.button.draw( - &printer - .offset(position) - .cropped(size) - .focused(self.focus == DialogFocus::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); - } - - return Some(buttons_height); } direction::Orientation::Vertical => { - // Current horizontal position of the next button - let mut buttons_width = 0; - - // Calculate the total height of buttons including spacing - let height = self + buttons_height = self .buttons .iter() .map(|button| button.button.size.y) .sum::() + self.buttons.len().saturating_sub(1); - - let max_size = self.buttons.iter().map(|button|button.button.size.x).max().unwrap_or_default(); - let overhead = self.padding + self.borders; - if printer.size.y < overhead.horizontal() { - return None; - } - let mut offset = overhead.top + max_size = self + .buttons + .iter() + .map(|button|button.button.size.x) + .max().unwrap_or_default(); + } + } + let mut button_size = Vec2::from_major_minor(self.button_orientation, buttons_height, buttons_width); + + let overhead = self.padding + self.borders; + if self.button_orientation == direction::Orientation::Horizontal && printer.size.x < overhead.horizontal() { + return None; + } + if self.button_orientation == direction::Orientation::Vertical && printer.size.y < overhead.horizontal() { + return None; + } + let offset_bottom = overhead.left + + self + .align + .h + .get_offset(buttons_width, printer.size.x - overhead.horizontal()); + let offset_right = overhead.top + self .align .v - .get_offset(height, printer.size.y - overhead.vertical()); - - let overhead_right = self.padding.right + self.borders.right + max_size; + .get_offset(buttons_height, printer.size.y - overhead.vertical()); + let mut offset = Vec2::from_major_minor(self.button_orientation, offset_bottom, offset_right).x; - let x = match printer.size.x.checked_sub(overhead_right) { - Some(x) => x, - 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(x, offset); - *button.offset.lock() = position; - button.button.draw( - &printer - .offset(position) - .cropped(size) - .focused(self.focus == DialogFocus::Button(i)), - ); - // Keep 1 blank between two buttons - offset += size.y + 1; - // Also keep 1 blank to the left of the buttons - buttons_width = max(buttons_width, size.x + 1); - } + let overhead_bottom = self.padding.bottom + self.borders.bottom + 1; + let overhead_right = self.padding.right + self.borders.right + max_size; - return Some(buttons_width); - } + let y = match printer.size.y.checked_sub(overhead_bottom) { + Some(y) => y, + None => return None, + }; + let x = match printer.size.x.checked_sub(overhead_right) { + Some(x) => x, + None => return None, + }; + let z = Vec2::from_major_minor(self.button_orientation, y, x).x; + + for (i, button) in self.buttons.iter().enumerate() { + let size = Vec2::from_major_minor(self.button_orientation, button.button.size.x, button.button.size.y); + + // Add some special effect to the focused button + let position = Vec2::from_major_minor(self.button_orientation, offset, z); + *button.offset.lock() = position; + button.button.draw( + &printer + .offset(position) + .cropped(button.button.size) + .focused(self.focus == DialogFocus::Button(i)), + ); + // Keep 1 blank between two buttons + offset += size.x + 1; + // Also keep 1 blank to the left or above the buttons + button_size.x = max(button_size.x, size.y + 1); } + + Some(button_size.x) } fn draw_content(&self, printer: &Printer, buttons_size: usize) { From d08bb733b285421238830611df29277ff19e509f Mon Sep 17 00:00:00 2001 From: "razvan.iliescu2912" Date: Sun, 17 Nov 2024 13:11:08 +0200 Subject: [PATCH 9/9] The draw implementation now has a single branch --- cursive-core/src/views/dialog.rs | 45 ++++++++++++-------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/cursive-core/src/views/dialog.rs b/cursive-core/src/views/dialog.rs index 8af160c9..bf6c1581 100644 --- a/cursive-core/src/views/dialog.rs +++ b/cursive-core/src/views/dialog.rs @@ -809,29 +809,15 @@ impl Dialog { impl View for Dialog { fn draw(&self, printer: &Printer) { - match self.button_orientation { - direction::Orientation::Horizontal => { - // 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); - } - direction::Orientation::Vertical => { - let buttons_width = match self.draw_buttons(printer) { - Some(width) => width, - None => return, - }; - + // Draw the buttons + let buttons_size = match self.draw_buttons(printer) { + Some(size) => size, + None => return, + }; + + if self.button_orientation == direction::Orientation::Vertical { // Adjust the content area size - let content_area_width = printer.size.x.saturating_sub(buttons_width + self.borders.horizontal()); + let content_area_width = printer.size.x.saturating_sub(buttons_size + self.borders.horizontal()); let content_area_height = printer.size.y.saturating_sub(self.borders.vertical()); // Check if there's enough space for the content @@ -846,14 +832,15 @@ impl View for Dialog { // Draw the content self.content.draw(&content_printer); - - // Print the borders - printer.print_box(Vec2::new(0, 0), printer.size, false); - - // Draw the title - self.draw_title(printer); - } + } else { + self.draw_content(printer, buttons_size); } + + // Print the borders + printer.print_box(Vec2::new(0, 0), printer.size, false); + + // Draw the title + self.draw_title(printer); } fn required_size(&mut self, req: Vec2) -> Vec2 {