use super::{actions::*, after::*, split_panel::*, which::*};
use {
cursive::{direction::*, event::*, theme::*, view::*, *},
std::cmp::*,
};
const TYPE_NAME: &str = "SplitPanel";
const TOP_LEFT: &str = "┌";
const BOTTOM_LEFT: &str = "└";
const TOP_RIGHT: &str = "┐";
const BOTTOM_RIGHT: &str = "┘";
const VERTICAL: &str = "│";
const VERTICAL_THICK: &str = "┃";
const HORIZONTAL: &str = "─";
const HORIZONTAL_THICK: &str = "━";
const TOP_HINGE: &str = "┰";
const BOTTOM_HINGE: &str = "┸";
const LEFT_HINGE: &str = "┝";
const RIGHT_HINGE: &str = "┥";
impl View for SplitPanel {
fn type_name(&self) -> &'static str {
TYPE_NAME
}
fn focus_view(&mut self, selector: &Selector) -> Result<EventResult, ViewNotFound> {
if let Ok(prior) = self.front.view.focus_view(selector) {
Ok(self.set_focus(Some(WhichPane::Front)).after(prior))
} else if let Ok(prior) = self.back.view.focus_view(selector) {
Ok(self.set_focus(Some(WhichPane::Back)).after(prior))
} else {
Err(ViewNotFound)
}
}
fn take_focus(&mut self, source: Direction) -> Result<EventResult, CannotFocus> {
match source.relative(self.orientation).unwrap_or(Relative::Front) {
Relative::Front => match self.front.view.take_focus(source) {
Ok(event_result) => Ok(event_result),
Err(_) => self.back.view.take_focus(source),
},
Relative::Back => match self.back.view.take_focus(source) {
Ok(event_result) => Ok(event_result),
Err(_) => self.front.view.take_focus(source),
},
}
}
fn needs_relayout(&self) -> bool {
self.needs_relayout || self.front.view.needs_relayout() || self.back.view.needs_relayout()
}
fn layout(&mut self, mut size: Vec2) {
self.size = Some(size);
if self.border {
size = size.checked_sub((2, 2)).unwrap_or_default();
}
let extent = *size.get(self.orientation);
let mut divider = self.layout_divider(extent);
self.front.start = 0;
self.front.extent = divider;
if self.visible_divider {
divider += 1;
}
self.back.start = divider;
if self.back.start >= extent {
self.back.start = 0;
self.back.extent = 0;
} else {
self.back.extent = extent - self.back.start;
}
self.front.layout(size, self.orientation);
self.back.layout(size, self.orientation);
self.needs_relayout = false;
}
fn required_size(&mut self, mut constraint: Vec2) -> Vec2 {
if self.border {
constraint = constraint.checked_sub((2, 2)).unwrap_or_default();
}
let front = self.front.required_size(constraint, self.orientation);
let back = self.back.required_size(constraint, self.orientation);
let size = Vec2::from(match self.orientation {
Orientation::Horizontal => (front.x + back.x, max(front.y, back.y)),
Orientation::Vertical => (max(front.x, back.x), front.y + back.y),
});
match (self.border, self.visible_divider) {
(true, true) => {
size + match self.orientation {
Orientation::Horizontal => (3, 2),
Orientation::Vertical => (2, 3),
}
}
(true, false) => size + (2, 2),
(false, true) => {
size + match self.orientation {
Orientation::Horizontal => (1, 0),
Orientation::Vertical => (0, 1),
}
}
(false, false) => size,
}
}
fn important_area(&self, size: Vec2) -> Rect {
match self.focus {
Some(WhichPane::Front) => self.front.important_area(size, self.orientation, self.border),
Some(WhichPane::Back) => self.back.important_area(size, self.orientation, self.border),
None => Rect::from_size((0, 0), size),
}
}
fn on_event(&mut self, event: Event) -> EventResult {
match if self.movable_divider { self.actions.get(&event) } else { None } {
Some(action) => match action {
Action::MoveDividerToFront => self.move_divider(-1),
Action::MoveDividerToBack => self.move_divider(1),
Action::ToggleBorder => {
self.set_border(!self.border);
EventResult::consumed()
}
Action::ToggleVisibleDivider => {
self.set_visible_divider(!self.visible_divider);
EventResult::consumed()
}
Action::ToggleMovableDivider => {
self.set_movable_divider(!self.movable_divider);
EventResult::consumed()
}
Action::ToggleOrientation => {
self.set_orientation(self.orientation.swap());
self.remove_action(Action::MoveDividerToFront);
self.remove_action(Action::MoveDividerToBack);
match self.orientation {
Orientation::Horizontal => {
self.set_action(Action::MoveDividerToFront, Event::Shift(Key::Left));
self.set_action(Action::MoveDividerToBack, Event::Shift(Key::Right));
}
Orientation::Vertical => {
self.set_action(Action::MoveDividerToFront, Event::Shift(Key::Up));
self.set_action(Action::MoveDividerToBack, Event::Shift(Key::Down));
}
}
EventResult::consumed()
}
},
None => match event {
Event::Mouse { offset, position, event } => {
if event.button() == Some(MouseButton::Left) && self.is_on_movable_divider(offset, position) {
self.moving_divider = true;
EventResult::consumed()
} else if self.moving_divider {
if matches!(event, MouseEvent::Release(_)) {
self.moving_divider = false;
} else if let Some(position) = position.checked_sub(offset) {
let mut divider = *position.get(self.orientation);
if self.border {
divider = divider.checked_sub(1).unwrap_or_default();
}
self.set_divider(divider);
}
EventResult::consumed()
} else if let Some((offset, position)) =
self.front.mouse_event(offset, position, self.orientation, self.border, self.size)
{
let prior = if event.grabs_focus() {
self.set_focus(Some(WhichPane::Front))
} else {
EventResult::Ignored
};
self.front.view.on_event(Event::Mouse { offset, position, event }).after(prior)
} else if let Some((offset, position)) =
self.back.mouse_event(offset, position, self.orientation, self.border, self.size)
{
let prior = if event.grabs_focus() {
self.set_focus(Some(WhichPane::Back))
} else {
EventResult::Ignored
};
self.back.view.on_event(Event::Mouse { offset, position, event }).after(prior)
} else {
EventResult::Ignored
}
}
event => match self.focus {
Some(WhichPane::Front) => self.front.view.on_event(event),
Some(WhichPane::Back) => self.back.view.on_event(event),
None => {
let prior = self.set_focus(Some(WhichPane::Front));
self.front.view.on_event(event).after(prior)
}
},
},
}
}
fn call_on_any(&mut self, selector: &Selector, callback: AnyCb) {
self.front.view.call_on_any(selector, callback);
self.back.view.call_on_any(selector, callback);
}
fn draw(&self, printer: &Printer) {
let mut _shrinked = None;
let printer =
if self.draw_lines(printer) { _shrinked.insert(printer.shrinked_centered((2, 2))) } else { printer };
self.front.draw(printer, self.orientation, self.is_focused(WhichPane::Front));
self.back.draw(printer, self.orientation, self.is_focused(WhichPane::Back));
}
}
impl SplitPanel {
fn is_focused(&self, which: WhichPane) -> bool {
self.focus.map(|focus| focus == which).unwrap_or_default()
}
fn set_focus(&mut self, focus: Option<WhichPane>) -> EventResult {
let old_focus = self.focus;
self.focus = focus;
if self.focus != old_focus
&& let Some(old_focus) = old_focus
{
match old_focus {
WhichPane::Front => self.front.view.on_event(Event::FocusLost),
WhichPane::Back => self.back.view.on_event(Event::FocusLost),
}
} else {
EventResult::consumed()
}
}
fn is_on_movable_divider(&self, offset: Vec2, position: Vec2) -> bool {
if self.visible_divider
&& self.movable_divider
&& !self.moving_divider
&& let Some(mut divider) = self.divider
&& let Some(size) = self.size
&& let Some(position) = position.checked_sub(offset)
{
if self.border {
divider += 1;
let corner = size.checked_sub((1, 1)).unwrap_or_default();
match self.orientation {
Orientation::Horizontal => position.y > 0 && position.y < corner.y && position.x == divider,
Orientation::Vertical => position.x > 0 && position.x < corner.x && position.y == divider,
}
} else {
match self.orientation {
Orientation::Horizontal => position.x == divider,
Orientation::Vertical => position.y == divider,
}
}
} else {
false
}
}
fn move_divider(&mut self, delta: isize) -> EventResult {
if self.movable_divider {
self.set_divider(self.divider.and_then(|divider| divider.checked_add_signed(delta)).unwrap_or_default());
EventResult::consumed()
} else {
EventResult::Ignored
}
}
fn layout_divider(&mut self, extent: usize) -> usize {
*self.divider.insert(match self.divider {
Some(divider) => min(divider, extent.checked_sub(1).unwrap_or_default()),
None => extent / 2,
})
}
fn draw_lines(&self, printer: &Printer) -> bool {
let corner = printer.size.checked_sub((1, 1)).unwrap_or_default();
let mut _divider_printer = None;
let divider_printer = if self.moving_divider {
let mut divider_printer = printer.clone();
divider_printer.set_effect(Effect::Reverse);
_divider_printer.insert(divider_printer)
} else {
printer
};
if self.border {
printer.print((0, 0), TOP_LEFT);
printer.print((corner.x, 0), TOP_RIGHT);
printer.print((0, corner.y), BOTTOM_LEFT);
printer.print(corner, BOTTOM_RIGHT);
if self.visible_divider
&& let Some(mut divider) = self.divider
{
divider = min(divider + 1, corner.get(self.orientation).checked_sub(1).unwrap_or_default());
match self.orientation {
Orientation::Horizontal => {
printer.print((divider, 0), TOP_HINGE);
printer.print((divider, corner.y), BOTTOM_HINGE);
if let Some(length) = divider.checked_sub(1) {
printer.print_hline((1, 0), length, HORIZONTAL);
printer.print_hline((1, corner.y), length, HORIZONTAL);
}
if let Some(length) = corner.x.checked_sub(divider + 1) {
printer.print_hline((divider + 1, 0), length, HORIZONTAL);
printer.print_hline((divider + 1, corner.y), length, HORIZONTAL);
}
if let Some(length) = corner.y.checked_sub(1) {
printer.print_vline((0, 1), length, VERTICAL);
divider_printer.print_vline((divider, 1), length, VERTICAL_THICK);
printer.print_vline((corner.x, 1), length, VERTICAL);
}
}
Orientation::Vertical => {
printer.print((0, divider), LEFT_HINGE);
printer.print((corner.x, divider), RIGHT_HINGE);
if let Some(length) = divider.checked_sub(1) {
printer.print_vline((0, 1), length, VERTICAL);
printer.print_vline((corner.x, 1), length, VERTICAL);
}
if let Some(length) = corner.y.checked_sub(divider + 1) {
printer.print_vline((0, divider + 1), length, VERTICAL);
printer.print_vline((corner.x, divider + 1), length, VERTICAL);
}
if let Some(length) = corner.x.checked_sub(1) {
printer.print_hline((1, 0), length, HORIZONTAL);
divider_printer.print_hline((1, divider), length, HORIZONTAL_THICK);
printer.print_hline((1, corner.y), length, HORIZONTAL);
}
}
}
} else {
if let Some(length) = corner.y.checked_sub(1) {
printer.print_vline((0, 1), length, VERTICAL);
printer.print_vline((corner.x, 1), length, VERTICAL);
}
if let Some(length) = corner.x.checked_sub(1) {
printer.print_hline((1, 0), length, HORIZONTAL);
printer.print_hline((1, corner.y), length, HORIZONTAL);
}
}
true
} else if self.visible_divider
&& let Some(divider) = self.divider
{
match self.orientation {
Orientation::Horizontal => divider_printer.print_vline((divider, 0), corner.y, VERTICAL),
Orientation::Vertical => divider_printer.print_hline((0, divider), corner.x, HORIZONTAL),
}
false
} else {
false
}
}
}