summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClementTsang <cjhtsang@uwaterloo.ca>2021-09-08 23:12:49 -0400
committerClementTsang <cjhtsang@uwaterloo.ca>2021-09-11 00:46:34 -0400
commite7b9c729123eda386f12ce90faaa413bf516fed3 (patch)
tree3db767c1d23c35aeebdcf9ee2c0a92009e9416e1
parent587987a2a5cfa9b946cab3f88df37cf7cc1b94fc (diff)
refactor: add general keybinds, fix buggy movement
Adds back some of the general program keybinds, and fixes both a bug causing widget movement via keybinds to be incorrect, and not correcting the last selected widget in the layout tree rows/cols after clicking/setting the default widget!
-rw-r--r--docs/content/usage/general-usage.md4
-rw-r--r--src/app.rs187
-rw-r--r--src/app/data_farmer.rs37
-rw-r--r--src/app/event.rs2
-rw-r--r--src/app/layout_manager.rs133
-rw-r--r--src/app/widgets.rs3
-rw-r--r--src/app/widgets/bottom_widgets/basic_mem.rs4
-rw-r--r--src/app/widgets/bottom_widgets/basic_net.rs1
-rw-r--r--src/app/widgets/bottom_widgets/cpu.rs150
-rw-r--r--src/app/widgets/bottom_widgets/mem.rs4
-rw-r--r--src/app/widgets/bottom_widgets/net.rs2
-rw-r--r--src/app/widgets/bottom_widgets/process.rs83
-rw-r--r--src/bin/main.rs5
-rw-r--r--src/canvas.rs2
-rw-r--r--src/constants.rs4
-rw-r--r--src/data_conversion.rs80
-rw-r--r--src/lib.rs56
17 files changed, 490 insertions, 267 deletions
diff --git a/docs/content/usage/general-usage.md b/docs/content/usage/general-usage.md
index 4bfcdb61..eb457aef 100644
--- a/docs/content/usage/general-usage.md
+++ b/docs/content/usage/general-usage.md
@@ -41,8 +41,8 @@ Note that key bindings are generally case-sensitive.
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| ++q++ , ++ctrl+c++ | Quit |
| ++esc++ | Close dialog windows, search, widgets, or exit expanded mode |
-| ++ctrl+r++ | Reset display and any collected data |
-| ++f++ | Freeze/unfreeze updating with new data |
+| ++ctrl+r++ | Resets any collected data |
+| ++f++ | Toggles freezing, which stops new data from being shown |
| ++question++ | Open help menu |
| ++e++ | Toggle expanding the currently selected widget |
| ++ctrl+up++ <br/> ++shift+up++ <br/> ++K++ <br/> ++W++ | Select the widget above |
diff --git a/src/app.rs b/src/app.rs
index 7e2451b6..b3409d3f 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -104,19 +104,29 @@ pub struct AppConfigFields {
pub no_write: bool,
pub show_table_scroll_position: bool,
pub is_advanced_kill: bool,
- // TODO: Remove these, move network details state-side.
pub network_unit_type: DataUnit,
pub network_scale_type: AxisScaling,
pub network_use_binary_prefix: bool,
}
+/// The [`FrozenState`] indicates whether the application state should be frozen; if it is, save a snapshot of
+/// the data collected at that instant.
+pub enum FrozenState {
+ NotFrozen,
+ Frozen(DataCollection),
+}
+
+impl Default for FrozenState {
+ fn default() -> Self {
+ Self::NotFrozen
+ }
+}
+
pub struct AppState {
pub dd_err: Option<String>,
to_delete_process_list: Option<(String, Vec<Pid>)>,
- pub is_frozen: bool,
-
pub canvas_data: canvas::DisplayableData,
pub data_collection: DataCollection,
@@ -161,6 +171,7 @@ pub struct AppState {
pub widget_lookup_map: FxHashMap<NodeId, TmpBottomWidget>,
pub layout_tree: Arena<LayoutNode>,
pub layout_tree_root: NodeId,
+ frozen_state: FrozenState,
}
impl AppState {
@@ -189,7 +200,6 @@ impl AppState {
// Use defaults.
dd_err: Default::default(),
to_delete_process_list: Default::default(),
- is_frozen: Default::default(),
canvas_data: Default::default(),
data_collection: Default::default(),
is_expanded: Default::default(),
@@ -211,37 +221,30 @@ impl AppState {
basic_mode_use_percent: Default::default(),
is_force_redraw: Default::default(),
is_determining_widget_boundary: Default::default(),
+ frozen_state: Default::default(),
}
}
- pub fn reset(&mut self) {
- // Reset multi
- self.reset_multi_tap_keys();
+ pub fn is_frozen(&self) -> bool {
+ matches!(self.frozen_state, FrozenState::Frozen(_))
+ }
- // Reset dialog state
- self.help_dialog_state.is_showing_help = false;
- self.delete_dialog_state.is_showing_dd = false;
+ pub fn toggle_freeze(&mut self) {
+ if matches!(self.frozen_state, FrozenState::Frozen(_)) {
+ self.frozen_state = FrozenState::NotFrozen;
+ } else {
+ self.frozen_state = FrozenState::Frozen(self.data_collection.clone());
+ }
+ }
- // Close all searches and reset it
- self.proc_state
- .widget_states
+ pub fn reset(&mut self) {
+ // Call reset on all widgets.
+ self.widget_lookup_map
.values_mut()
- .for_each(|state| {
- state.process_search_state.search_state.reset();
- });
- self.proc_state.force_update_all = true;
-
- // Clear current delete list
- self.to_delete_process_list = None;
- self.dd_err = None;
+ .for_each(|widget| widget.reset());
// Unfreeze.
- self.is_frozen = false;
-
- // Reset zoom
- self.reset_cpu_zoom();
- self.reset_mem_zoom();
- self.reset_net_zoom();
+ self.frozen_state = FrozenState::NotFrozen;
// Reset data
self.data_collection.reset();
@@ -259,10 +262,77 @@ impl AppState {
self.dd_err = None;
}
+ /// Handles a global event involving a char.
+ fn handle_global_char(&mut self, c: char) -> EventResult {
+ if c.is_ascii_control() {
+ EventResult::NoRedraw
+ } else {
+ // Check for case-sensitive bindings first.
+ match c {
+ 'H' | 'A' => self.move_to_widget(MovementDirection::Left),
+ 'L' | 'D' => self.move_to_widget(MovementDirection::Right),
+ 'K' | 'W' => self.move_to_widget(MovementDirection::Up),
+ 'J' | 'S' => self.move_to_widget(MovementDirection::Down),
+ _ => {
+ let c = c.to_ascii_lowercase();
+ match c {
+ 'q' => EventResult::Quit,
+ 'e' => {
+ if self.app_config_fields.use_basic_mode {
+ EventResult::NoRedraw
+ } else {
+ self.is_expanded = !self.is_expanded;
+ EventResult::Redraw
+ }
+ }
+ '?' => {
+ self.help_dialog_state.is_showing_help = true;
+ EventResult::Redraw
+ }
+ 'f' => {
+ self.toggle_freeze();
+ if !self.is_frozen() {
+ let data_collection = &self.data_collection;
+ self.widget_lookup_map
+ .iter_mut()
+ .for_each(|(_id, widget)| widget.update_data(data_collection));
+ }
+ EventResult::Redraw
+ }
+ _ => EventResult::NoRedraw,
+ }
+ }
+ }
+ }
+ }
+
+ /// Moves to a widget.
+ fn move_to_widget(&mut self, direction: MovementDirection) -> EventResult {
+ let layout_tree = &mut self.layout_tree;
+ let previous_selected = self.selected_widget;
+ if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
+ match move_widget_selection(layout_tree, widget, self.selected_widget, direction) {
+ MoveWidgetResult::ForceRedraw(new_widget_id) => {
+ self.selected_widget = new_widget_id;
+ EventResult::Redraw
+ }
+ MoveWidgetResult::NodeId(new_widget_id) => {
+ self.selected_widget = new_widget_id;
+
+ if previous_selected != self.selected_widget {
+ EventResult::Redraw
+ } else {
+ EventResult::NoRedraw
+ }
+ }
+ }
+ } else {
+ EventResult::NoRedraw
+ }
+ }
+
/// Handles a global [`KeyEvent`], and returns an [`EventResult`].
fn handle_global_shortcut(&mut self, event: KeyEvent) -> EventResult {
- // TODO: Write this.
-
if event.modifiers.is_empty() {
match event.code {
KeyCode::Esc => {
@@ -280,24 +350,33 @@ impl AppState {
EventResult::NoRedraw
}
}
- KeyCode::Char('q') => EventResult::Quit,
- KeyCode::Char('e') => {
- if self.app_config_fields.use_basic_mode {
- EventResult::NoRedraw
- } else {
- self.is_expanded = !self.is_expanded;
- EventResult::Redraw
- }
- }
- KeyCode::Char('?') => {
- self.help_dialog_state.is_showing_help = true;
+ KeyCode::Char(c) => self.handle_global_char(c),
+ _ => EventResult::NoRedraw,
+ }
+ } else if let KeyModifiers::CONTROL = event.modifiers {
+ match event.code {
+ KeyCode::Char('c') | KeyCode::Char('C') => EventResult::Quit,
+ KeyCode::Char('r') | KeyCode::Char('R') => {
+ self.reset();
+ let data_collection = &self.data_collection;
+ self.widget_lookup_map
+ .iter_mut()
+ .for_each(|(_id, widget)| widget.update_data(data_collection));
EventResult::Redraw
}
+ KeyCode::Left => self.move_to_widget(MovementDirection::Left),
+ KeyCode::Right => self.move_to_widget(MovementDirection::Right),
+ KeyCode::Up => self.move_to_widget(MovementDirection::Up),
+ KeyCode::Down => self.move_to_widget(MovementDirection::Down),
_ => EventResult::NoRedraw,
}
- } else if let KeyModifiers::CONTROL = event.modifiers {
+ } else if let KeyModifiers::SHIFT = event.modifiers {
match event.code {
- KeyCode::Char('c') => EventResult::Quit,
+ KeyCode::Left => self.move_to_widget(MovementDirection::Left),
+ KeyCode::Right => self.move_to_widget(MovementDirection::Right),
+ KeyCode::Up => self.move_to_widget(MovementDirection::Up),
+ KeyCode::Down => self.move_to_widget(MovementDirection::Down),
+ KeyCode::Char(c) => self.handle_global_char(c),
_ => EventResult::NoRedraw,
}
} else {
@@ -317,7 +396,14 @@ impl AppState {
}
ReturnSignal::Update => {
if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
- widget.update_data(&self.data_collection);
+ match &self.frozen_state {
+ FrozenState::NotFrozen => {
+ widget.update_data(&self.data_collection);
+ }
+ FrozenState::Frozen(frozen_data) => {
+ widget.update_data(frozen_data);
+ }
+ }
}
EventResult::Redraw
}
@@ -379,7 +465,11 @@ impl AppState {
if was_id_already_selected {
return self.convert_widget_event_result(result);
} else {
- // If the weren't equal, *force* a redraw.
+ // If the weren't equal, *force* a redraw, and correct the layout tree.
+ correct_layout_last_selections(
+ &mut self.layout_tree,
+ self.selected_widget,
+ );
let _ = self.convert_widget_event_result(result);
return EventResult::Redraw;
}
@@ -402,7 +492,7 @@ impl AppState {
BottomEvent::Update(new_data) => {
self.data_collection.eat_data(new_data);
- if !self.is_frozen {
+ if !self.is_frozen() {
let data_collection = &self.data_collection;
self.widget_lookup_map
.iter_mut()
@@ -1599,12 +1689,7 @@ impl AppState {
'G' => self.skip_to_last(),
'k' => self.on_up_key(),
'j' => self.on_down_key(),
- 'f' => {
- self.is_frozen = !self.is_frozen;
- if self.is_frozen {
- self.data_collection.set_frozen_time();
- }
- }
+ 'f' => {}
'C' => {
// self.open_config(),
}
diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs
index 25d265ab..c2747289 100644
--- a/src/app/data_farmer.rs
+++ b/src/app/data_farmer.rs
@@ -26,7 +26,7 @@ use regex::Regex;
pub type TimeOffset = f64;
pub type Value = f64;
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
pub struct TimedData {
pub rx_data: Value,
pub tx_data: Value,
@@ -41,14 +41,14 @@ pub struct TimedData {
/// collected, and what is needed to convert into a displayable form.
///
/// If the app is *frozen* - that is, we do not want to *display* any changing
-/// data, keep updating this, don't convert to canvas displayable data!
+/// data, keep updating this. As of 2021-09-08, we just clone the current collection
+/// when it freezes to have a snapshot floating around.
///
/// Note that with this method, the *app* thread is responsible for cleaning -
/// not the data collector.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
pub struct DataCollection {
pub current_instant: Instant,
- pub frozen_instant: Option<Instant>,
pub timed_data_vec: Vec<(Instant, TimedData)>,
pub network_harvest: network::NetworkHarvest,
pub memory_harvest: memory::MemHarvest,
@@ -70,7 +70,6 @@ impl Default for DataCollection {
fn default() -> Self {
DataCollection {
current_instant: Instant::now(),
- frozen_instant: None,
timed_data_vec: Vec::default(),
network_harvest: network::NetworkHarvest::default(),
memory_harvest: memory::MemHarvest::default(),
@@ -92,21 +91,19 @@ impl Default for DataCollection {
impl DataCollection {
pub fn reset(&mut self) {
- self.timed_data_vec = Vec::default();
- self.network_harvest = network::NetworkHarvest::default();
- self.memory_harvest = memory::MemHarvest::default();
- self.swap_harvest = memory::MemHarvest::default();
- self.cpu_harvest = cpu::CpuHarvest::default();
- self.process_harvest = Vec::default();
- self.disk_harvest = Vec::default();
- self.io_harvest = disks::IoHarvest::default();
- self.io_labels_and_prev = Vec::default();
- self.temp_harvest = Vec::default();
- self.battery_harvest = Vec::default();
- }
-
- pub fn set_frozen_time(&mut self) {
- self.frozen_instant = Some(self.current_instant);
+ self.timed_data_vec = Default::default();
+ self.network_harvest = Default::default();
+ self.memory_harvest = Default::default();
+ self.swap_harvest = Default::default();
+ self.cpu_harvest = Default::default();
+ self.process_harvest = Default::default();
+ self.process_name_pid_map = Default::default();
+ self.process_cmd_pid_map = Default::default();
+ self.disk_harvest = Default::default();
+ self.io_harvest = Default::default();
+ self.io_labels_and_prev = Default::default();
+ self.temp_harvest = Default::default();
+ self.battery_harvest = Default::default();
}
pub fn clean_data(&mut self, max_time_millis: u64) {
diff --git a/src/app/event.rs b/src/app/event.rs
index a6afb2d8..9201e2c0 100644
--- a/src/app/event.rs
+++ b/src/app/event.rs
@@ -45,7 +45,7 @@ pub enum WidgetEventResult {
/// How a widget should handle a widget selection request.
pub enum SelectionAction {
- /// This event occurs if the widget internally handled the selection action.
+ /// This event occurs if the widget internally handled the selection action. A redraw is required.
Handled,
/// This event occurs if the widget did not handle the selection action; the caller must handle it.
NotHandled,
diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs
index 9b88fb8a..61d17924 100644
--- a/src/app/layout_manager.rs
+++ b/src/app/layout_manager.rs
@@ -997,9 +997,9 @@ Supported widget names:
// --- New stuff ---
/// Represents a row in the layout tree.
-#[derive(PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RowLayout {
- last_selected_index: usize,
+ last_selected: Option<NodeId>,
pub parent_rule: LayoutRule,
pub bound: Rect,
}
@@ -1007,7 +1007,7 @@ pub struct RowLayout {
impl RowLayout {
fn new(parent_rule: LayoutRule) -> Self {
Self {
- last_selected_index: 0,
+ last_selected: None,
parent_rule,
bound: Rect::default(),
}
@@ -1015,9 +1015,9 @@ impl RowLayout {
}
/// Represents a column in the layout tree.
-#[derive(PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ColLayout {
- last_selected_index: usize,
+ last_selected: Option<NodeId>,
pub parent_rule: LayoutRule,
pub bound: Rect,
}
@@ -1025,7 +1025,7 @@ pub struct ColLayout {
impl ColLayout {
fn new(parent_rule: LayoutRule) -> Self {
Self {
- last_selected_index: 0,
+ last_selected: None,
parent_rule,
bound: Rect::default(),
}
@@ -1033,7 +1033,7 @@ impl ColLayout {
}
/// Represents a widget in the layout tree.
-#[derive(PartialEq, Eq, Clone, Default)]
+#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct WidgetLayout {
pub bound: Rect,
}
@@ -1042,7 +1042,7 @@ pub struct WidgetLayout {
/// - [`LayoutNode::Row`] (a non-leaf that distributes its children horizontally)
/// - [`LayoutNode::Col`] (a non-leaf node that distributes its children vertically)
/// - [`LayoutNode::Widget`] (a leaf node that contains the ID of the widget it is associated with)
-#[derive(PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LayoutNode {
/// A non-leaf that distributes its children horizontally
Row(RowLayout),
@@ -1382,6 +1382,8 @@ pub fn create_layout_tree(
));
}
+ correct_layout_last_selections(&mut arena, selected);
+
Ok(LayoutCreationOutput {
layout_tree: arena,
root: root_id,
@@ -1391,6 +1393,35 @@ pub fn create_layout_tree(
})
}
+/// We may have situations where we also have to make sure the correct layout indices are selected.
+/// For example, when we select a widget by clicking, we want to update the layout so that it's as if a user
+/// manually moved to it via keybinds.
+///
+/// We can do this by just going through the ancestors, starting from the widget itself.
+pub fn correct_layout_last_selections(arena: &mut Arena<LayoutNode>, selected: NodeId) {
+ let mut selected_ancestors = selected.ancestors(&arena).collect::<Vec<_>>();
+ let prev_node = selected_ancestors.pop();
+ if let Some(mut prev_node) = prev_node {
+ for node in selected_ancestors {
+ if let Some(layout_node) = arena.get_mut(node).map(|n| n.get_mut()) {
+ match layout_node {
+ LayoutNode::Row(RowLayout { last_selected, .. })
+ | LayoutNode::Col(ColLayout { last_selected, .. }) => {
+ *last_selected = Some(prev_node);
+ }
+ LayoutNode::Widget(_) => {}
+ }
+ }
+ prev_node = node;
+ }
+ }
+}
+
+pub enum MoveWidgetResult {
+ ForceRedraw(NodeId),
+ NodeId(NodeId),
+}
+
/// Attempts to find and return the selected [`BottomWidgetId`] after moving in a direction.
///
/// Note this function assumes a properly built tree - if not, bad things may happen! We generally assume that:
@@ -1399,7 +1430,7 @@ pub fn create_layout_tree(
pub fn move_widget_selection(
layout_tree: &mut Arena<LayoutNode>, current_widget: &mut TmpBottomWidget,
current_widget_id: NodeId, direction: MovementDirection,
-) -> NodeId {
+) -> MoveWidgetResult {
// We first give our currently-selected widget a chance to react to the movement - it may handle it internally!
let handled = match direction {
MovementDirection::Left => current_widget.handle_widget_selection_left(),
@@ -1408,16 +1439,18 @@ pub fn move_widget_selection(
MovementDirection::Down => current_widget.handle_widget_selection_down(),
};
+ // TODO: Do testing.
+
match handled {
SelectionAction::Handled => {
// If it was handled by the widget, then we don't have to do anything - return the current one.
- current_widget_id
+ MoveWidgetResult::ForceRedraw(current_widget_id)
}
SelectionAction::NotHandled => {
/// Keeps traversing up the `layout_tree` until it hits a parent where `current_id` is a child and parent
/// is a [`LayoutNode::Row`], returning its parent's [`NodeId`] and the child's [`NodeId`] (in that order).
/// If this crawl fails (i.e. hits a root, it is an invalid tree for some reason), it returns [`None`].
- fn find_first_row(
+ fn find_parent_row(
layout_tree: &Arena<LayoutNode>, current_id: NodeId,
) -> Option<(NodeId, NodeId)> {
layout_tree
@@ -1430,7 +1463,7 @@ pub fn move_widget_selection(
})
.and_then(|(parent_id, parent_node)| match parent_node.get() {
LayoutNode::Row(_) => Some((parent_id, current_id)),
- LayoutNode::Col(_) => find_first_row(layout_tree, parent_id),
+ LayoutNode::Col(_) => find_parent_row(layout_tree, parent_id),
LayoutNode::Widget(_) => None,
})
}
@@ -1438,7 +1471,7 @@ pub fn move_widget_selection(
/// Keeps traversing up the `layout_tree` until it hits a parent where `current_id` is a child and parent
/// is a [`LayoutNode::Col`], returning its parent's [`NodeId`] and the child's [`NodeId`] (in that order).
/// If this crawl fails (i.e. hits a root, it is an invalid tree for some reason), it returns [`None`].
- fn find_first_col(
+ fn find_parent_col(
layout_tree: &Arena<LayoutNode>, current_id: NodeId,
) -> Option<(NodeId, NodeId)> {
layout_tree
@@ -1450,7 +1483,7 @@ pub fn move_widget_selection(
.map(|parent_node| (parent_id, parent_node))
})
.and_then(|(parent_id, parent_node)| match parent_node.get() {
- LayoutNode::Row(_) => find_first_col(layout_tree, parent_id),
+ LayoutNode::Row(_) => find_parent_col(layout_tree, parent_id),
LayoutNode::Col(_) => Some((parent_id, current_id)),
LayoutNode::Widget(_) => None,
})
@@ -1461,21 +1494,19 @@ pub fn move_widget_selection(
if let Some(current_node) = layout_tree.get(current_id) {
match current_node.get() {
LayoutNode::Row(RowLayout {
- last_selected_index,
+ last_selected,
parent_rule: _,
bound: _,
})
| LayoutNode::Col(ColLayout {
- last_selected_index,
+ last_selected,
parent_rule: _,
bound: _,
}) => {
- if let Some(next_child) =
- current_id.children(layout_tree).nth(*last_selected_index)
- {
+ if let Some(next_child) = *last_selected {
descend_to_leaf(layout_tree, next_child)
} else {
- current_id
+ current_node.first_child().unwrap_or(current_id)
}
}
LayoutNode::Widget(_) => {
@@ -1493,7 +1524,7 @@ pub fn move_widget_selection(
// on the tree layout to help us decide where to go.
// Movement logic is inspired by i3. When we enter a new column/row, we go to the *last* selected
// element; if we can't, go to the nearest one.
- match direction {
+ let proposed_id = match direction {
MovementDirection::Left => {
// When we move "left":
// 1. Look for the parent of the current widget.
@@ -1513,7 +1544,8 @@ pub fn move_widget_selection(
fn find_left(
layout_tree: &mut Arena<LayoutNode>, current_id: NodeId,
) -> NodeId {
- if let Some((parent_id, child_id)) = find_first_row(layout_tree, current_id)
+ if let Some((parent_id, child_id)) =
+ find_parent_row(layout_tree, current_id)
{
if let Some(prev_sibling) =
child_id.preceding_siblings(layout_tree).nth(1)
@@ -1521,16 +1553,17 @@ pub fn move_widget_selection(
// Subtract one from the currently selected index...
if let Some(parent) = layout_tree.get_mut(parent_id) {
if let LayoutNode::Row(row) = parent.get_mut() {
- row.last_selected_index =
- row.last_selected_index.saturating_sub(1);
+ row.last_selected = Some(prev_sibling);
}
}
// Now descend downwards!
descend_to_leaf(layout_tree, prev_sibling)
- } else {
+ } else if parent_id != current_id {
// Darn, we can't go further back! Recurse on this ID.
- find_left(layout_tree, child_id)
+ find_left(layout_tree, parent_id)
+ } else {
+ current_id
}
} else {
// Failed, just return the current ID.
@@ -1546,23 +1579,26 @@ pub fn move_widget_selection(
fn find_right(
layout_tree: &mut Arena<LayoutNode>, current_id: NodeId,
) -> NodeId {
- if let Some((parent_id, child_id)) = find_first_row(layout_tree, current_id)
+ if let Some((parent_id, child_id)) =
+ find_parent_row(layout_tree, current_id)
{
- if let Some(prev_sibling) =
+ if let Some(following_sibling) =
child_id.following_siblings(layout_tree).nth(1)
{
// Add one to the currently selected index...
if let Some(parent) = layout_tree.get_mut(parent_id) {
if let LayoutNode::Row(row) = parent.get_mut() {
- row.last_selected_index += 1;
+ row.last_selected = Some(following_sibling);
}
}
// Now descend downwards!
- descend_to_leaf(layout_tree, prev_sibling)
- } else {
+ descend_to_leaf(layout_tree, following_sibling)
+ } else if parent_id != current_id {
// Darn, we can't go further back! Recurse on this ID.
- find_right(layout_tree, child_id)
+ find_right(layout_tree, parent_id)
+ } else {
+ current_id
}
} else {
// Failed, just return the current ID.
@@ -1578,7 +1614,8 @@ pub fn move_widget_selection(
fn find_above(
layout_tree: &mut Arena<LayoutNode>, current_id: NodeId,
) -> NodeId {
- if let Some((parent_id, child_id)) = find_first_col(layout_tree, current_id)
+ if let Some((parent_id, child_id)) =
+ find_parent_col(layout_tree, current_id)
{
if let Some(prev_sibling) =
child_id.preceding_siblings(layout_tree).nth(1)
@@ -1586,16 +1623,17 @@ pub fn move_widget_selection(
// Subtract one from the currently selected index...
if let Some(parent) = layout_tree.get_mut(parent_id) {
if let LayoutNode::Col(row) = parent.get_mut() {
- row.last_selected_index =
- row.last_selected_index.saturating_sub(1);
+ row.last_selected = Some(prev_sibling);
}
}
// Now descend downwards!
descend_to_leaf(layout_tree, prev_sibling)
- } else {
+ } else if parent_id != current_id {
// Darn, we can't go further back! Recurse on this ID.
- find_above(layout_tree, child_id)
+ find_above(layout_tree, parent_id)
+ } else {
+ current_id
}
} else {
// Failed, just return the current ID.
@@ -1611,23 +1649,26 @@ pub fn move_widget_selection(
fn find_below(
layout_tree: &mut Arena<LayoutNode>, current_id: NodeId,
) -> NodeId {
- if let Some((parent_id, child_id)) = find_first_col(layout_tree, current_id)
+ if let Some((parent_id, child_id)) =
+ find_parent_col(layout_tree, current_id)
{
- if let Some(prev_sibling) =
+ if let Some(following_sibling) =
child_id.following_siblings(layout_tree).nth(1)
{
// Add one to the currently selected index...
if let Some(parent) = layout_tree.get_mut(parent_id) {
if let LayoutNode::Col(row) = parent.get_mut() {
- row.last_selected_index += 1;
+ row.last_selected = Some(following_sibling);
}
}
// Now descend downwards!
- descend_to_leaf(layout_tree, prev_sibling)
- } else {
+ descend_to_leaf(layout_tree, following_sibling)
+ } else if parent_id != current_id {
// Darn, we can't go further back! Recurse on this ID.
- find_below(layout_tree, child_id)
+ find_below(layout_tree, parent_id)
+ } else {
+ current_id
}
} else {
// Failed, just return the current ID.
@@ -1636,6 +1677,12 @@ pub fn move_widget_selection(
}
find_below(layout_tree, current_widget_id)
}
+ };
+
+ if let Some(LayoutNode::Widget(_)) = layout_tree.get(proposed_id).map(|n| n.get()) {
+ MoveWidgetResult::NodeId(proposed_id)
+ } else {
+ MoveWidgetResult::NodeId(current_widget_id)
}
}
}
diff --git a/src/app/widgets.rs b/src/app/widgets.rs
index cac6a032..ffc08cd6 100644
--- a/src/app/wi