#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
use std::sync::Mutex;
use crate::ported::crt::{
ColorElements, KEY_CTRL, KEY_DOWN, KEY_END, KEY_ENTER, KEY_F, KEY_HOME, KEY_MOUSE, KEY_NPAGE,
KEY_PPAGE, KEY_RECLICK, KEY_UP,
};
use crate::ported::dynamicscreen::DynamicScreen;
use crate::ported::functionbar::{FunctionBar, FunctionBar_new};
use crate::ported::hashtable::Hashtable_foreach;
use crate::ported::lineeditor::{
LineEditor, LineEditor_getCursor, LineEditor_getText, LineEditor_handleKey,
LineEditor_initWithMax, LineEditor_setText,
};
use crate::ported::listitem::{
ListItem, ListItem_compare, ListItem_display, ListItem_init, ListItem_new,
};
use crate::ported::object::{Object, ObjectClass, Object_class};
use crate::ported::panel::{
HandlerResult, Panel, PanelClass, Panel_add, Panel_done, Panel_getSelectedIndex, Panel_insert,
Panel_new, Panel_onKey, Panel_prune, Panel_selectByTyping, Panel_setCursorToSelection,
Panel_setDefaultBar, Panel_setHeader, Panel_setSelected, Panel_setSelectionColor,
EVENT_PANEL_LOST_FOCUS, EVENT_SET_SELECTED,
};
use crate::ported::richstring::RichString;
use crate::ported::screenmanager::ScreenManager;
use crate::ported::screenspanel::SCREEN_NAME_LEN;
use crate::ported::settings::{
ScreenDefaults, Settings, Settings_newDynamicScreen, Settings_newScreen,
};
use crate::ported::xutils::String_eq;
const NEWLINE: i32 = '\n' as i32;
const CARRIAGE_RETURN: i32 = '\r' as i32;
const ESC: i32 = 27;
const EQUALS: i32 = b'=' as i32;
const KEY_F2: i32 = KEY_F(2);
const KEY_F5: i32 = KEY_F(5);
const KEY_F10: i32 = KEY_F(10);
const CTRL_N: i32 = KEY_CTRL(b'N' as i32);
pub struct ScreenNamesPanel {
pub super_: Panel,
pub scr: *mut ScreenManager,
pub settings: *mut Settings,
pub editor: LineEditor,
pub ds: *mut DynamicScreen,
pub saved: Option<String>,
pub renamingItem: Option<usize>,
}
impl PanelClass for ScreenNamesPanel {
fn as_panel(&self) -> &Panel {
&self.super_
}
fn as_panel_mut(&mut self) -> &mut Panel {
&mut self.super_
}
fn event_handler(&mut self, ev: i32) -> HandlerResult {
ScreenNamesPanel_eventHandler(self, ev)
}
}
pub struct ScreenNameListItem {
pub super_: ListItem,
pub ss: Option<usize>,
}
pub struct ScreenTabsPanel {
pub super_: Panel,
pub scr: *mut ScreenManager,
pub settings: *mut Settings,
pub names: *mut ScreenNamesPanel,
pub cursor: i32,
}
impl PanelClass for ScreenTabsPanel {
fn as_panel(&self) -> &Panel {
&self.super_
}
fn as_panel_mut(&mut self) -> &mut Panel {
&mut self.super_
}
fn event_handler(&mut self, ev: i32) -> HandlerResult {
ScreenTabsPanel_eventHandler(self, ev)
}
}
pub struct ScreenTabListItem {
pub super_: ListItem,
pub ds: *mut DynamicScreen,
}
static ScreenTabListItem_class: ObjectClass = ObjectClass {
extends: Some(&Object_class),
};
impl Object for ScreenTabListItem {
fn klass(&self) -> &'static ObjectClass {
&ScreenTabListItem_class
}
fn display(&self, out: &mut RichString) {
ListItem_display(&self.super_, out);
}
fn compare(&self, other: &dyn Object) -> i32 {
let any: &dyn core::any::Any = other;
let o = any
.downcast_ref::<ScreenTabListItem>()
.expect("ScreenTabListItem_compare called across incompatible classes");
ListItem_compare(&self.super_, &o.super_)
}
}
static ScreenNameListItem_class: ObjectClass = ObjectClass {
extends: Some(&Object_class),
};
impl Object for ScreenNameListItem {
fn klass(&self) -> &'static ObjectClass {
&ScreenNameListItem_class
}
fn display(&self, out: &mut RichString) {
ListItem_display(&self.super_, out);
}
fn compare(&self, other: &dyn Object) -> i32 {
let any: &dyn core::any::Any = other;
let o = any
.downcast_ref::<ScreenNameListItem>()
.expect("ScreenNameListItem_compare called across incompatible classes");
ListItem_compare(&self.super_, &o.super_)
}
}
impl ScreenNamesPanel {
fn set_item_value(&mut self, idx: usize, value: String) {
let obj: &mut dyn core::any::Any = self.super_.items[idx].object_mut();
if let Some(item) = obj.downcast_mut::<ScreenNameListItem>() {
item.super_.value = value;
return;
}
if let Some(item) = obj.downcast_mut::<ListItem>() {
item.value = value;
}
}
fn item_value(&self, idx: usize) -> String {
let obj: &dyn core::any::Any = self.super_.items[idx].object();
if let Some(item) = obj.downcast_ref::<ScreenNameListItem>() {
return item.super_.value.clone();
}
if let Some(item) = obj.downcast_ref::<ListItem>() {
return item.value.clone();
}
String::new()
}
}
const ScreenNamesFunctions: [&str; 10] = [
" ", " ", " ", " ", "New ", " ", " ", " ", " ",
"Done ",
];
const ScreenNamesRenamingFunctions: [&str; 10] = [
" ", "Cancel", " ", " ", " ", " ", " ", " ", " ",
"Done ",
];
static ScreenNames_renamingBar: Mutex<Option<FunctionBar>> = Mutex::new(None);
pub fn ScreenNamesPanel_fill(this: &mut ScreenNamesPanel, ds: Option<&DynamicScreen>) {
let settings_ptr = this.settings;
Panel_prune(&mut this.super_);
let settings: &Settings = unsafe { &*settings_ptr };
for i in 0..settings.screens.len() {
let ss = &settings.screens[i];
match ds {
None => {
if ss.dynamic.is_some() {
continue;
}
}
Some(ds) => {
match &ss.dynamic {
None => continue,
Some(dynamic) => {
if !String_eq(&ds.name, dynamic) {
continue;
}
}
}
}
}
let heading = ss.heading.as_deref().unwrap_or("");
Panel_add(&mut this.super_, Box::new(ListItem_new(heading, i as i32)));
}
this.ds = ds.map_or(std::ptr::null_mut(), |d| {
d as *const DynamicScreen as *mut DynamicScreen
});
}
pub fn ScreenTabsPanel_delete(this: ScreenTabsPanel) {
let ScreenTabsPanel { super_, .. } = this;
Panel_done(super_);
}
pub fn ScreenTabsPanel_eventHandler(this: &mut ScreenTabsPanel, ch: i32) -> HandlerResult {
let mut result = HandlerResult::IGNORED;
let mut selected = Panel_getSelectedIndex(&this.super_);
match ch {
EVENT_SET_SELECTED => {
result = HandlerResult::HANDLED;
}
KEY_F5 | CTRL_N => {
let names = unsafe { &mut *this.names };
return ScreenNamesPanel_eventHandlerNormal(names, ch);
}
KEY_UP | KEY_DOWN | KEY_NPAGE | KEY_PPAGE | KEY_HOME | KEY_END => {
let previous = selected;
Panel_onKey(&mut this.super_, ch);
selected = Panel_getSelectedIndex(&this.super_);
if previous != selected {
result = HandlerResult::HANDLED;
}
}
_ => {
if (0..255).contains(&ch) && (ch as u8).is_ascii_alphabetic() {
result = Panel_selectByTyping(&mut this.super_, ch);
}
if result == HandlerResult::BREAK_LOOP {
result = HandlerResult::IGNORED;
}
}
}
if result == HandlerResult::HANDLED {
let focus_ds: Option<*mut DynamicScreen> = if this.super_.items.is_empty() {
None
} else {
let sel = Panel_getSelectedIndex(&this.super_) as usize;
let any: &dyn core::any::Any = this.super_.items[sel].object();
let focus = any
.downcast_ref::<ScreenTabListItem>()
.expect("ScreenTabsPanel_eventHandler: panel row is not a ScreenTabListItem");
Some(focus.ds)
};
if let Some(ds) = focus_ds {
let names = unsafe { &mut *this.names };
let ds_ref = if ds.is_null() {
None
} else {
Some(unsafe { &*ds })
};
ScreenNamesPanel_fill(names, ds_ref);
}
}
result
}
pub fn ScreenTabListItem_new(value: &str, ds: *mut DynamicScreen) -> ScreenTabListItem {
let mut this = ScreenTabListItem {
super_: ListItem {
value: String::new(),
key: 0,
moving: false,
},
ds,
};
ListItem_init(&mut this.super_, value, 0);
this
}
pub fn addDynamicScreen(_key: u32, screen: &DynamicScreen, super_: &mut Panel) {
let name = screen.heading.as_deref().unwrap_or(&screen.name);
let ds = screen as *const DynamicScreen as *mut DynamicScreen;
Panel_add(super_, Box::new(ScreenTabListItem_new(name, ds)));
}
const ScreenTabsFunctions: [&str; 10] = [
" ", " ", " ", " ", "New ", " ", " ", " ", " ",
"Done ",
];
pub unsafe fn ScreenTabsPanel_new(settings: *mut Settings) -> ScreenTabsPanel {
let fuBar = FunctionBar_new(Some(&ScreenTabsFunctions), None, None);
let super_ = Panel_new(1, 1, 1, 1, Some(fuBar));
let names = Box::into_raw(Box::new(unsafe { ScreenNamesPanel_new(settings) }));
let mut this = ScreenTabsPanel {
super_,
scr: std::ptr::null_mut(),
settings,
names,
cursor: 0,
};
this.super_.cursorOn = false;
this.cursor = 0;
Panel_setHeader(&mut this.super_, "Screen tabs");
let ds_ht = unsafe { &*settings }
.dynamicScreens
.expect("ScreenTabsPanel_new: settings->dynamicScreens is NULL");
Panel_add(
&mut this.super_,
Box::new(ScreenTabListItem_new("Processes", std::ptr::null_mut())),
);
let dyn_screens = unsafe { &*ds_ht };
let super_mut = &mut this.super_;
Hashtable_foreach(dyn_screens, &mut |key, value| {
let any: &dyn core::any::Any = value;
let screen = any
.downcast_ref::<DynamicScreen>()
.expect("ScreenTabsPanel_new: dynamicScreens value is not a DynamicScreen");
addDynamicScreen(key, screen, super_mut);
});
this
}
pub fn ScreenNameListItem_new(value: &str, ss: Option<usize>) -> ScreenNameListItem {
let mut this = ScreenNameListItem {
super_: ListItem {
value: String::new(),
key: 0,
moving: false,
},
ss,
};
ListItem_init(&mut this.super_, value, 0);
this
}
pub fn ScreenTabsPanel_cleanup() {
let mut bar = ScreenNames_renamingBar.lock().unwrap();
if bar.is_some() {
*bar = None;
}
}
pub fn ScreenNamesPanel_delete(this: ScreenNamesPanel) {
let ScreenNamesPanel { super_, .. } = this;
Panel_done(super_);
}
pub fn renameScreenSettings(this: &mut ScreenNamesPanel, item: usize) {
let (ss_index, value) = {
let any: &dyn core::any::Any = this.super_.items[item].object();
let nameItem = any
.downcast_ref::<ScreenNameListItem>()
.expect("renameScreenSettings: panel row is not a ScreenNameListItem");
(nameItem.ss, nameItem.super_.value.clone())
};
let settings: &mut Settings = unsafe { &mut *this.settings };
if let Some(idx) = ss_index {
settings.screens[idx].heading = Some(value);
}
settings.changed = true;
settings.lastUpdate = settings.lastUpdate.wrapping_add(1);
}
pub fn ScreenNamesPanel_eventHandlerRenaming(
this: &mut ScreenNamesPanel,
ch: i32,
) -> HandlerResult {
let mut do_finish = false;
match ch {
EVENT_SET_SELECTED => {
let sel = Panel_getSelectedIndex(&this.super_);
if this.super_.items.is_empty() || this.renamingItem != Some(sel as usize) {
do_finish = true;
}
}
EVENT_PANEL_LOST_FOCUS => {
do_finish = true;
}
NEWLINE | CARRIAGE_RETURN | KEY_ENTER | KEY_F10 => {
if !this.super_.items.is_empty() {
do_finish = true;
}
}
ESC | KEY_F2 => {
if this.super_.items.is_empty() {
return HandlerResult::HANDLED;
}
let idx = Panel_getSelectedIndex(&this.super_) as usize;
let saved = this.saved.take().unwrap_or_default();
this.set_item_value(idx, saved);
this.renamingItem = None;
this.super_.cursorOn = false;
Panel_setSelectionColor(&mut this.super_, ColorElements::PANEL_SELECTION_FOCUS);
Panel_setDefaultBar(&mut this.super_);
return HandlerResult::HANDLED;
}
_ => {
if ch == EQUALS {
return HandlerResult::HANDLED;
}
LineEditor_handleKey(&mut this.editor, ch);
this.super_.selectedLen = LineEditor_getCursor(&this.editor);
Panel_setCursorToSelection(&mut this.super_);
if let Some(idx) = this.renamingItem {
let text = LineEditor_getText(&this.editor).to_string();
this.set_item_value(idx, text);
}
return HandlerResult::HANDLED;
}
}
if do_finish {
let idx = match this.renamingItem {
Some(idx) => idx,
None => return HandlerResult::HANDLED,
};
this.saved = None;
let text = LineEditor_getText(&this.editor).to_string();
this.set_item_value(idx, text);
this.super_.cursorOn = false;
Panel_setSelectionColor(&mut this.super_, ColorElements::PANEL_SELECTION_FOCUS);
Panel_setDefaultBar(&mut this.super_);
renameScreenSettings(this, idx);
this.renamingItem = None;
}
HandlerResult::HANDLED
}
pub fn startRenaming(this: &mut ScreenNamesPanel) {
if this.super_.items.is_empty() {
return;
}
let idx = Panel_getSelectedIndex(&this.super_) as usize;
this.renamingItem = Some(idx);
this.super_.cursorOn = true;
let name = this.item_value(idx);
this.saved = Some(name.clone());
LineEditor_initWithMax(&mut this.editor, SCREEN_NAME_LEN - 1);
LineEditor_setText(&mut this.editor, &name);
let text = LineEditor_getText(&this.editor).to_string();
this.set_item_value(idx, text);
Panel_setSelectionColor(&mut this.super_, ColorElements::PANEL_EDIT);
this.super_.selectedLen = LineEditor_getCursor(&this.editor);
Panel_setCursorToSelection(&mut this.super_);
this.super_.currentBar = ScreenNames_renamingBar.lock().unwrap().clone();
}
pub fn addNewScreen(this: &mut ScreenNamesPanel, ds: *mut DynamicScreen) {
let name = "New";
let ss: usize = if !ds.is_null() {
let settings = unsafe { &mut *this.settings };
Settings_newDynamicScreen(settings, name, unsafe { &*ds }, None)
} else {
let settings = unsafe { &mut *this.settings };
Settings_newScreen(
settings,
&ScreenDefaults {
name: Some(name),
columns: Some("PID Command"),
sortKey: Some("PID"),
treeSortKey: None,
},
)
};
let item = ScreenNameListItem_new(name, Some(ss));
let idx = Panel_getSelectedIndex(&this.super_);
Panel_insert(&mut this.super_, idx + 1, Box::new(item));
Panel_setSelected(&mut this.super_, idx + 1);
}
pub fn ScreenNamesPanel_eventHandlerNormal(this: &mut ScreenNamesPanel, ch: i32) -> HandlerResult {
let oldFocus = if this.super_.items.is_empty() {
None
} else {
Some(Panel_getSelectedIndex(&this.super_))
};
let mut result = HandlerResult::IGNORED;
match ch {
NEWLINE | CARRIAGE_RETURN | KEY_ENTER | KEY_MOUSE | KEY_RECLICK => {
Panel_setSelectionColor(&mut this.super_, ColorElements::PANEL_SELECTION_FOCUS);
result = HandlerResult::HANDLED;
}
EVENT_SET_SELECTED => {
result = HandlerResult::HANDLED;
}
KEY_NPAGE | KEY_PPAGE | KEY_HOME | KEY_END => {
Panel_onKey(&mut this.super_, ch);
}
KEY_F5 | CTRL_N => {
let ds = this.ds;
addNewScreen(this, ds);
startRenaming(this);
result = HandlerResult::HANDLED;
}
_ => {
if (0..255).contains(&ch) && (ch as u8).is_ascii_alphabetic() {
result = Panel_selectByTyping(&mut this.super_, ch);
}
if result == HandlerResult::BREAK_LOOP {
result = HandlerResult::IGNORED;
}
}
}
let newFocus = if this.super_.items.is_empty() {
None
} else {
Some(Panel_getSelectedIndex(&this.super_))
};
if newFocus.is_some() && oldFocus != newFocus {
result = HandlerResult::HANDLED;
}
result
}
pub fn ScreenNamesPanel_eventHandler(this: &mut ScreenNamesPanel, ch: i32) -> HandlerResult {
if this.renamingItem.is_none() {
ScreenNamesPanel_eventHandlerNormal(this, ch)
} else {
ScreenNamesPanel_eventHandlerRenaming(this, ch)
}
}
pub unsafe fn ScreenNamesPanel_new(settings: *mut Settings) -> ScreenNamesPanel {
let fuBar = FunctionBar_new(Some(&ScreenNamesFunctions), None, None);
{
let mut bar = ScreenNames_renamingBar.lock().unwrap();
if bar.is_none() {
*bar = Some(FunctionBar_new(
Some(&ScreenNamesRenamingFunctions),
None,
None,
));
}
}
let mut this = ScreenNamesPanel {
super_: Panel_new(1, 1, 1, 1, Some(fuBar)),
scr: std::ptr::null_mut(),
settings,
editor: LineEditor::default(),
ds: std::ptr::null_mut(),
saved: None,
renamingItem: None,
};
LineEditor_initWithMax(&mut this.editor, SCREEN_NAME_LEN - 1);
this.super_.cursorOn = false;
Panel_setHeader(&mut this.super_, "Screens");
let s: &Settings = unsafe { &*settings };
for i in 0..s.screens.len() {
let ss = &s.screens[i];
if ss.dynamic.is_some() {
continue;
}
let heading = ss.heading.as_deref().unwrap_or("").to_string();
Panel_add(
&mut this.super_,
Box::new(ScreenNameListItem_new(&heading, Some(i))),
);
}
this
}
#[cfg(test)]
use crate::ported::panel::PanelItem;
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::lineeditor::{LineEditor_initWithMax, LineEditor_setText};
use crate::ported::panel::Panel_new;
use crate::ported::settings::{HeaderLayout, ScreenSettings};
fn bar() -> FunctionBar {
FunctionBar {
functions: vec![" ".into()],
keys: vec!["F5".into()],
events: vec![5],
staticData: false,
}
}
#[test]
fn cleanup_drops_the_renaming_bar_and_nulls_it() {
*ScreenNames_renamingBar.lock().unwrap() = Some(bar());
assert!(ScreenNames_renamingBar.lock().unwrap().is_some());
ScreenTabsPanel_cleanup();
assert!(ScreenNames_renamingBar.lock().unwrap().is_none());
ScreenTabsPanel_cleanup();
assert!(ScreenNames_renamingBar.lock().unwrap().is_none());
}
fn settings_with(screens: Vec<ScreenSettings>) -> Settings {
Settings {
hLayout: HeaderLayout::HF_TWO_50_50,
hColumns: Vec::new(),
screens,
ssIndex: 0,
changed: false,
lastUpdate: 0,
..Default::default()
}
}
fn builtin(heading: &str) -> ScreenSettings {
ScreenSettings {
heading: Some(heading.to_string()),
dynamic: None,
..Default::default()
}
}
fn dynamic(heading: &str, dyn_name: &str) -> ScreenSettings {
ScreenSettings {
heading: Some(heading.to_string()),
dynamic: Some(dyn_name.to_string()),
..Default::default()
}
}
fn names_panel(settings: &mut Settings) -> ScreenNamesPanel {
ScreenNamesPanel {
super_: Panel_new(1, 1, 1, 1, None),
scr: std::ptr::null_mut(),
settings: settings as *mut Settings,
editor: LineEditor::default(),
ds: std::ptr::null_mut(),
saved: None,
renamingItem: None,
}
}
fn tabs_panel(names: &mut ScreenNamesPanel) -> ScreenTabsPanel {
ScreenTabsPanel {
super_: Panel_new(1, 1, 1, 1, None),
scr: std::ptr::null_mut(),
settings: std::ptr::null_mut(),
names: names as *mut ScreenNamesPanel,
cursor: 0,
}
}
fn li(value: &str, key: i32) -> Box<dyn Object> {
Box::new(ListItem_new(value, key))
}
fn name_item(value: &str, ss: Option<usize>) -> Box<dyn Object> {
Box::new(ScreenNameListItem {
super_: ListItem_new(value, 0),
ss,
})
}
fn tab_item(value: &str, ds: *mut DynamicScreen) -> Box<dyn Object> {
Box::new(ScreenTabListItem {
super_: ListItem_new(value, 0),
ds,
})
}
fn items(p: &ScreenNamesPanel) -> Vec<(String, i32)> {
p.super_
.items
.iter()
.map(|o| {
let any: &dyn std::any::Any = o.object();
let li = any.downcast_ref::<ListItem>().expect("fill adds ListItems");
(li.value.clone(), li.key)
})
.collect()
}
fn name_value(p: &ScreenNamesPanel, idx: usize) -> String {
let any: &dyn std::any::Any = p.super_.items[idx].object();
any.downcast_ref::<ScreenNameListItem>()
.unwrap()
.super_
.value
.clone()
}
fn enter_renaming(p: &mut ScreenNamesPanel, idx: usize) {
let original = name_value(p, idx);
p.renamingItem = Some(idx);
p.saved = Some(original.clone());
p.super_.cursorOn = true;
LineEditor_initWithMax(&mut p.editor, 19); LineEditor_setText(&mut p.editor, &original);
let text = LineEditor_getText(&p.editor).to_string();
p.set_item_value(idx, text);
}
#[test]
fn fill_builtin_selects_non_dynamic_screens_with_index_keys() {
let mut settings = settings_with(vec![
builtin("Main"), dynamic("Pods", "pods"), builtin("I/O"), ]);
let mut panel = names_panel(&mut settings);
ScreenNamesPanel_fill(&mut panel, None);
assert_eq!(
items(&panel),
vec![("Main".to_string(), 0), ("I/O".to_string(), 2)]
);
assert!(panel.ds.is_null());
}
#[test]
fn fill_dynamic_selects_only_matching_name() {
let mut settings = settings_with(vec![
builtin("Main"), dynamic("Pods", "pods"), dynamic("Containers", "ctr"), dynamic("More Pods", "pods"), ]);
let mut panel = names_panel(&mut settings);
let ds = DynamicScreen {
name: "pods".to_string(),
heading: None,
columnKeys: None,
direction: 1,
};
ScreenNamesPanel_fill(&mut panel, Some(&ds));
assert_eq!(
items(&panel),
vec![("Pods".to_string(), 1), ("More Pods".to_string(), 3)]
);
assert_eq!(
panel.ds as *const DynamicScreen,
&ds as *const DynamicScreen
);
}
#[test]
fn fill_prunes_existing_items_first() {
let mut settings = settings_with(vec![builtin("Only")]);
let mut panel = names_panel(&mut settings);
Panel_add(&mut panel.super_, li("stale", 99));
Panel_add(&mut panel.super_, li("stale2", 98));
ScreenNamesPanel_fill(&mut panel, None);
assert_eq!(items(&panel), vec![("Only".to_string(), 0)]);
}
#[test]
fn fill_empty_screens_yields_empty_panel() {
let mut settings = settings_with(Vec::new());
let mut panel = names_panel(&mut settings);
ScreenNamesPanel_fill(&mut panel, None);
assert!(panel.super_.items.is_empty());
}
#[test]
fn rename_screen_settings_writes_heading_and_dirties() {
let mut settings = settings_with(vec![builtin("Main"), builtin("I/O")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Renamed", Some(1))));
renameScreenSettings(&mut panel, 0);
assert_eq!(settings.screens[1].heading.as_deref(), Some("Renamed"));
assert_eq!(settings.screens[0].heading.as_deref(), Some("Main"));
assert!(settings.changed);
assert_eq!(settings.lastUpdate, 1);
}
#[test]
fn screen_tab_list_item_new_builds_row_with_ds_and_key_zero() {
let mut ds = DynamicScreen {
name: "pods".to_string(),
heading: None,
columnKeys: None,
direction: 1,
};
let item = ScreenTabListItem_new("Pods", &mut ds as *mut DynamicScreen);
assert_eq!(item.super_.value, "Pods");
assert_eq!(item.super_.key, 0); assert!(!item.super_.moving);
assert_eq!(item.ds, &mut ds as *mut DynamicScreen);
}
#[test]
fn screen_name_list_item_new_builds_row_with_ss_index() {
let item = ScreenNameListItem_new("Main", Some(3));
assert_eq!(item.super_.value, "Main");
assert_eq!(item.super_.key, 0);
assert_eq!(item.ss, Some(3));
let item = ScreenNameListItem_new("New", None);
assert_eq!(item.ss, None);
}
#[test]
fn start_renaming_enters_edit_mode_on_selected_row() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
panel.super_.selected = 0;
startRenaming(&mut panel);
assert_eq!(panel.renamingItem, Some(0));
assert_eq!(panel.saved.as_deref(), Some("Main")); assert!(panel.super_.cursorOn);
assert_eq!(LineEditor_getText(&panel.editor), "Main");
assert_eq!(name_value(&panel, 0), "Main"); assert_eq!(panel.super_.selectionColorId, ColorElements::PANEL_EDIT);
assert_eq!(panel.super_.selectedLen, 4); }
#[test]
fn start_renaming_on_empty_panel_is_a_noop() {
let mut settings = settings_with(Vec::new());
let mut panel = names_panel(&mut settings);
startRenaming(&mut panel);
assert_eq!(panel.renamingItem, None);
assert!(panel.saved.is_none());
assert!(!panel.super_.cursorOn);
}
#[test]
fn renaming_default_key_edits_editor_and_row_value() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
enter_renaming(&mut panel, 0);
let r = ScreenNamesPanel_eventHandlerRenaming(&mut panel, b'X' as i32);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(LineEditor_getText(&panel.editor), "MainX");
assert_eq!(name_value(&panel, 0), "MainX"); assert_eq!(panel.super_.selectedLen, 5); }
#[test]
fn renaming_equals_is_swallowed_without_editing() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
enter_renaming(&mut panel, 0);
let r = ScreenNamesPanel_eventHandlerRenaming(&mut panel, EQUALS);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(LineEditor_getText(&panel.editor), "Main");
assert_eq!(name_value(&panel, 0), "Main");
}
#[test]
fn renaming_esc_cancels_and_restores_original_name() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
enter_renaming(&mut panel, 0);
ScreenNamesPanel_eventHandlerRenaming(&mut panel, b'Z' as i32);
assert_eq!(name_value(&panel, 0), "MainZ");
let r = ScreenNamesPanel_eventHandlerRenaming(&mut panel, ESC);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(name_value(&panel, 0), "Main"); assert_eq!(panel.renamingItem, None);
assert!(!panel.super_.cursorOn);
assert_eq!(
panel.super_.selectionColorId,
ColorElements::PANEL_SELECTION_FOCUS
);
assert_eq!(settings.screens[0].heading.as_deref(), Some("Main"));
assert!(!settings.changed);
}
#[test]
fn renaming_enter_commits_edit_to_screen_settings() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
enter_renaming(&mut panel, 0);
ScreenNamesPanel_eventHandlerRenaming(&mut panel, b'Z' as i32);
let r = ScreenNamesPanel_eventHandlerRenaming(&mut panel, KEY_ENTER);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(name_value(&panel, 0), "MainZ");
assert_eq!(settings.screens[0].heading.as_deref(), Some("MainZ"));
assert!(settings.changed);
assert_eq!(settings.lastUpdate, 1);
assert_eq!(panel.renamingItem, None);
assert!(panel.saved.is_none());
assert!(!panel.super_.cursorOn);
}
#[test]
fn renaming_event_set_selected_same_row_keeps_editing() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
panel.super_.selected = 0;
enter_renaming(&mut panel, 0);
let r = ScreenNamesPanel_eventHandlerRenaming(&mut panel, EVENT_SET_SELECTED);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(panel.renamingItem, Some(0)); assert!(!settings.changed); }
#[test]
fn normal_event_set_selected_is_handled() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
let r = ScreenNamesPanel_eventHandlerNormal(&mut panel, EVENT_SET_SELECTED);
assert_eq!(r, HandlerResult::HANDLED);
}
#[test]
fn normal_enter_restores_focus_color_and_handles() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
Panel_setSelectionColor(&mut panel.super_, ColorElements::PANEL_EDIT);
let r = ScreenNamesPanel_eventHandlerNormal(&mut panel, KEY_ENTER);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(
panel.super_.selectionColorId,
ColorElements::PANEL_SELECTION_FOCUS
);
}
#[test]
fn normal_navigation_reports_handled_when_focus_moves() {
let mut settings = settings_with(Vec::new());
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("a", Some(0))));
panel
.super_
.items
.push(PanelItem::Owned(name_item("b", Some(1))));
panel
.super_
.items
.push(PanelItem::Owned(name_item("c", Some(2))));
panel.super_.selected = 0;
let r = ScreenNamesPanel_eventHandlerNormal(&mut panel, KEY_END);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(panel.super_.selected, 2);
}
#[test]
fn normal_typing_selects_by_prefix() {
let mut settings = settings_with(Vec::new());
let mut panel = names_panel(&mut settings);
panel.super_.items.push(PanelItem::Owned(li("apple", 0)));
panel.super_.items.push(PanelItem::Owned(li("banana", 1)));
panel.super_.selected = 0;
let r = ScreenNamesPanel_eventHandlerNormal(&mut panel, b'b' as i32);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(panel.super_.selected, 1); }
#[test]
fn normal_new_screen_arm_adds_a_screen() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
let before = panel.super_.items.len();
let r = ScreenNamesPanel_eventHandlerNormal(&mut panel, KEY_F5);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(panel.super_.items.len(), before + 1);
}
#[test]
fn dispatch_routes_to_normal_when_not_renaming() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
let r = ScreenNamesPanel_eventHandler(&mut panel, EVENT_SET_SELECTED);
assert_eq!(r, HandlerResult::HANDLED);
}
#[test]
fn dispatch_routes_to_renaming_when_renaming() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut panel = names_panel(&mut settings);
panel
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
enter_renaming(&mut panel, 0);
let r = ScreenNamesPanel_eventHandler(&mut panel, b'!' as i32);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(LineEditor_getText(&panel.editor), "Main!");
}
#[test]
fn tabs_event_set_selected_fills_names_with_builtin_screens() {
let mut settings = settings_with(vec![
builtin("Main"),
dynamic("Pods", "pods"),
builtin("I/O"),
]);
let mut names = names_panel(&mut settings);
let mut tabs = tabs_panel(&mut names);
Panel_add(
&mut tabs.super_,
tab_item("Processes", std::ptr::null_mut()),
);
let r = ScreenTabsPanel_eventHandler(&mut tabs, EVENT_SET_SELECTED);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(
items(&names),
vec![("Main".to_string(), 0), ("I/O".to_string(), 2)]
);
}
#[test]
fn tabs_navigation_fills_names_for_selected_dynamic_tab() {
let mut settings = settings_with(vec![
builtin("Main"),
dynamic("Pods", "pods"),
dynamic("Containers", "ctr"),
]);
let mut names = names_panel(&mut settings);
let mut ds_pods = DynamicScreen {
name: "pods".to_string(),
heading: None,
columnKeys: None,
direction: 1,
};
let mut tabs = tabs_panel(&mut names);
Panel_add(
&mut tabs.super_,
tab_item("Processes", std::ptr::null_mut()),
);
Panel_add(
&mut tabs.super_,
tab_item("Pods", &mut ds_pods as *mut DynamicScreen),
);
let r = ScreenTabsPanel_eventHandler(&mut tabs, KEY_DOWN);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(tabs.super_.selected, 1);
assert_eq!(items(&names), vec![("Pods".to_string(), 1)]);
}
#[test]
fn tabs_new_screen_arm_delegates_and_adds() {
let mut settings = settings_with(vec![builtin("Main")]);
let mut names = names_panel(&mut settings);
names
.super_
.items
.push(PanelItem::Owned(name_item("Main", Some(0))));
let before = names.super_.items.len();
let mut tabs = tabs_panel(&mut names);
Panel_add(
&mut tabs.super_,
tab_item("Processes", std::ptr::null_mut()),
);
let r = ScreenTabsPanel_eventHandler(&mut tabs, KEY_F5);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(names.super_.items.len(), before + 1);
}
}