cursive-split-panel 0.0.6

Split panel for the Cursive TUI library
Documentation
use super::{actions::*, pane::*, which::*};

use cursive::{direction::*, event::*, view::*, views::*, *};

//
// SplitPanel
//

/// Split panel.
///
/// Contains two panes with an optionally movable divider between them and an optional border.
/// (Both features are enabled by default.)
///
/// Can be oriented horizontally (the default) or vertically.
///
/// When [movable_divider](Self::movable_divider) is true will support the following
/// events (configurable):
///
/// * Shift+Left/Shift+Up: move the divider towards the front
/// * Shift+Right/Shift+Down: move the divider towards the back
/// * Mouse dragging of the divider
pub struct SplitPanel {
    pub(crate) orientation: Orientation,
    pub(crate) border: bool,
    pub(crate) visible_divider: bool,
    pub(crate) movable_divider: bool,
    pub(crate) front: Pane,
    pub(crate) back: Pane,
    pub(crate) divider: Option<usize>,
    pub(crate) needs_relayout: bool,
    pub(crate) size: Option<Vec2>,
    pub(crate) focus: Option<WhichPane>,
    pub(crate) moving_divider: bool,
    pub(crate) actions: Actions,
}

impl SplitPanel {
    /// Constructor.
    pub fn new(orientation: Orientation) -> Self {
        Self {
            orientation,
            border: true,
            visible_divider: true,
            movable_divider: true,
            front: DummyView.into_boxed_view().into(),
            back: DummyView.into_boxed_view().into(),
            divider: None,
            needs_relayout: true,
            size: None,
            focus: None,
            moving_divider: false,
            actions: Action::defaults(orientation),
        }
    }

    /// Constructor.
    pub fn horizontal() -> Self {
        Self::new(Orientation::Horizontal)
    }

    /// Constructor.
    pub fn vertical() -> Self {
        Self::new(Orientation::Vertical)
    }

    /// Orienation.
    pub fn orientation(&self) -> Orientation {
        self.orientation
    }

    /// Set orientation.
    pub fn set_orientation(&mut self, orientation: Orientation) {
        let old_orientation = self.orientation;
        self.orientation = orientation;
        if self.orientation != old_orientation {
            self.needs_relayout = true;
        }
    }

    /// Set orientation.
    ///
    /// Chainable.
    pub fn with_orientation(self, orientation: Orientation) -> Self {
        self.with(|self_| self_.set_orientation(orientation))
    }

    /// Whether to draw a border.
    pub fn border(&self) -> bool {
        self.border
    }

    /// Set whether to draw a border.
    pub fn set_border(&mut self, border: bool) {
        let old_border = self.border;
        self.border = border;
        if self.border != old_border {
            self.needs_relayout = true;
        }
    }

    /// Set whether to draw a border.
    ///
    /// Chainable.
    pub fn with_border(self, border: bool) -> Self {
        self.with(|self_| self_.set_border(border))
    }

    /// Whether the divider is visible.
    pub fn visible_divider(&self) -> bool {
        self.visible_divider
    }

    /// Set whether the divider is visible.
    pub fn set_visible_divider(&mut self, visible_divider: bool) {
        let old_visible_divider = self.border;
        self.visible_divider = visible_divider;
        if self.visible_divider != old_visible_divider {
            self.needs_relayout = true;
        }
    }

    /// Set whether the divider is visible.
    ///
    /// Chainable.
    pub fn with_visible_divider(self, visible_divider: bool) -> Self {
        self.with(|self_| self_.set_visible_divider(visible_divider))
    }

    /// Whether to allow the divider to move.
    pub fn movable_divider(&self) -> bool {
        self.movable_divider
    }

    /// Set whether to allow the divider to move.
    pub fn set_movable_divider(&mut self, movable_divider: bool) {
        self.movable_divider = movable_divider;
    }

    /// Set whether to allow the divider to move.
    ///
    /// Chainable.
    pub fn with_movable_divider(self, movable_divider: bool) -> Self {
        self.with(|self_| self_.set_movable_divider(movable_divider))
    }

    /// View for front panel.
    pub fn front(&self) -> &dyn View {
        self.front.view.as_ref()
    }

    /// View for front panel.
    pub fn front_mut(&mut self) -> &mut dyn View {
        self.front.view.as_mut()
    }

    /// Set view for front panel.
    pub fn set_front<ViewT>(&mut self, view: ViewT)
    where
        ViewT: 'static + IntoBoxedView,
    {
        self.front = view.into();
        self.needs_relayout = true;
    }

    /// Set view for front panel.
    ///
    /// Chainable.
    pub fn with_front<ViewT>(self, view: ViewT) -> Self
    where
        ViewT: 'static + IntoBoxedView,
    {
        self.with(|self_| self_.set_front(view))
    }

    /// View for back panel.
    pub fn back(&self) -> &dyn View {
        self.back.view.as_ref()
    }

    /// View for back panel.
    pub fn back_mut(&mut self) -> &mut dyn View {
        self.back.view.as_mut()
    }

    /// Set view for back panel.
    pub fn set_back<ViewT>(&mut self, view: ViewT)
    where
        ViewT: 'static + IntoBoxedView,
    {
        self.back = view.into();
        self.needs_relayout = true;
    }

    /// Set view for back panel.
    ///
    /// Chainable.
    pub fn with_back<ViewT>(self, view: ViewT) -> Self
    where
        ViewT: 'static + IntoBoxedView,
    {
        self.with(|self_| self_.set_back(view))
    }

    /// The divider position as the distance from the front border.
    ///
    /// Returns [None] if not yet set.
    pub fn divider(&self) -> Option<usize> {
        self.divider
    }

    /// Set divider position as the distance from the front border.
    ///
    /// Note that if it doesn't fit it will move during layout.
    pub fn set_divider(&mut self, divider: usize) {
        let old_divider = self.divider;
        self.divider = Some(divider);
        if self.divider != old_divider {
            self.needs_relayout = true;
        }
    }

    /// Set divider position as the distance from the front border.
    ///
    /// Note that if it doesn't fit it will move during layout.
    ///
    /// Chainable.
    pub fn with_divider(self, divider: usize) -> Self {
        self.with(|self_| self_.set_divider(divider))
    }

    /// Action map.
    pub fn actions(&self) -> &Actions {
        &self.actions
    }

    /// Action map.
    pub fn actions_mut(&mut self) -> &mut Actions {
        &mut self.actions
    }

    /// Set action map.
    pub fn set_actions(&mut self, actions: Actions) {
        self.actions = actions;
    }

    /// Set action map.
    ///
    /// Chainable.
    pub fn with_actions(self, actions: Actions) -> Self {
        self.with(|self_| self_.set_actions(actions))
    }

    /// Set action.
    ///
    /// Note that actions can be associated with more than one event.
    pub fn set_action<EventT>(&mut self, action: Action, event: EventT)
    where
        EventT: Into<Event>,
    {
        self.actions.insert(event.into(), action);
    }

    /// Set action.
    ///
    /// Note that actions can be associated with more than one event.
    ///
    /// Chainable.
    pub fn with_action<EventT>(self, action: Action, event: EventT) -> Self
    where
        EventT: Into<Event>,
    {
        self.with(|self_| self_.set_action(action, event))
    }

    /// Remove action.
    ///
    /// Will remove all associated events.
    ///
    /// Return true if removed.
    pub fn remove_action(&mut self, action: Action) -> bool {
        action.remove(&mut self.actions)
    }

    /// Remove action.
    ///
    /// Will remove all associated events.
    ///
    /// Chainable.
    pub fn without_action(self, action: Action) -> Self {
        self.with(|self_| _ = self_.remove_action(action))
    }
}

impl Default for SplitPanel {
    fn default() -> Self {
        Self::new(Orientation::Horizontal)
    }
}