#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
use std::sync::Mutex;
use crate::ported::availablecolumnspanel::{
AvailableColumnsPanel, AvailableColumnsPanel_fill, AvailableColumnsPanel_new,
};
use crate::ported::columnspanel::{ColumnsPanel, ColumnsPanel_fill, ColumnsPanel_new};
use crate::ported::crt::{
ColorElements, KEY_DC, KEY_DEL_MAC, KEY_DOWN, KEY_END, KEY_ENTER, KEY_HOME, KEY_MOUSE,
KEY_NPAGE, KEY_PPAGE, KEY_RECLICK, KEY_UP,
};
use crate::ported::functionbar::{FunctionBar, FunctionBar_new};
use crate::ported::hashtable::Hashtable;
use crate::ported::lineeditor::{
LineEditor, LineEditor_getCursor, LineEditor_getText, LineEditor_handleKey,
LineEditor_initWithMax, LineEditor_setText,
};
use crate::ported::listitem::{
ListItem, ListItem_compare, ListItem_delete, ListItem_display, ListItem_new,
};
use crate::ported::object::{Object, ObjectClass};
use crate::ported::panel::{
HandlerResult, Panel, PanelClass, Panel_add, Panel_delete, Panel_get, Panel_getSelectedIndex,
Panel_insert, Panel_moveSelectedDown, Panel_moveSelectedUp, Panel_new, Panel_onKey,
Panel_remove, Panel_selectByTyping, Panel_setCursorToSelection, Panel_setDefaultBar,
Panel_setHeader, Panel_setSelected, Panel_setSelectionColor, Panel_size,
EVENT_PANEL_LOST_FOCUS, EVENT_SET_SELECTED,
};
use crate::ported::richstring::RichString;
use crate::ported::screenmanager::{ScreenManager, ScreenManager_add};
use crate::ported::settings::{ScreenDefaults, ScreenSettings, Settings, Settings_newScreen};
pub const SCREEN_NAME_LEN: usize = 20;
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 = crate::ported::crt::KEY_F(2);
const KEY_F5: i32 = crate::ported::crt::KEY_F(5);
const KEY_F7: i32 = crate::ported::crt::KEY_F(7);
const KEY_F8: i32 = crate::ported::crt::KEY_F(8);
const KEY_F9: i32 = crate::ported::crt::KEY_F(9);
const KEY_F10: i32 = crate::ported::crt::KEY_F(10);
const KEY_CTRL_R: i32 = crate::ported::crt::KEY_CTRL('R' as i32);
const KEY_CTRL_N: i32 = crate::ported::crt::KEY_CTRL('N' as i32);
const LBRACKET: i32 = b'[' as i32;
const RBRACKET: i32 = b']' as i32;
const MINUS: i32 = b'-' as i32;
const PLUS: i32 = b'+' as i32;
const ScreensFunctions: [&str; 10] = [
" ", "Rename", " ", " ", "New ", " ", "MoveUp", "MoveDn", "Remove",
"Done ",
];
const DynamicFunctions: [&str; 10] = [
" ", "Rename", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove",
"Done ",
];
const ScreensRenamingFunctions: [&str; 10] = [
" ", "Cancel", " ", " ", " ", " ", " ", " ", " ",
"Done ",
];
static Screens_renamingBar: Mutex<Option<FunctionBar>> = Mutex::new(None);
pub struct ScreenListItem {
pub super_: ListItem,
pub ssIndex: usize,
}
static ScreenListItem_class: ObjectClass = ObjectClass {
extends: Some(&crate::ported::object::Object_class),
};
impl Object for ScreenListItem {
fn klass(&self) -> &'static ObjectClass {
&ScreenListItem_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::<ScreenListItem>()
.expect("ScreenListItem_compare called across incompatible classes");
ListItem_compare(&self.super_, &o.super_)
}
}
pub fn ScreenListItem_delete(this: ScreenListItem) {
let ScreenListItem { super_, ssIndex } = this;
let _ = ssIndex;
ListItem_delete(super_);
}
pub fn ScreenListItem_new(value: &str, ssIndex: usize) -> ScreenListItem {
ScreenListItem {
super_: ListItem_new(value, 0),
ssIndex,
}
}
pub fn ScreensPanel_cleanup() {
let mut bar = Screens_renamingBar.lock().unwrap();
if bar.is_some() {
*bar = None;
}
}
pub fn ScreensPanel_cancelMoving(this: &mut ScreensPanel) {
let super_ = &mut this.super_;
let size = Panel_size(super_);
for i in 0..size {
let obj: &mut dyn Object = super_.items[i as usize].object_mut();
let any: &mut dyn core::any::Any = obj;
if let Some(item) = any.downcast_mut::<ScreenListItem>() {
item.super_.moving = false;
}
}
this.moving = false;
Panel_setSelectionColor(&mut this.super_, ColorElements::PANEL_SELECTION_FOCUS);
}
pub fn ScreensPanel_delete(this: ScreensPanel) {
let ScreensPanel { super_, .. } = this;
Panel_delete(super_);
}
pub fn ScreensPanel_eventHandlerRenaming(this: &mut ScreensPanel, 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 | crate::ported::crt::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);
if this.renamingNewItem {
let rm = Panel_getSelectedIndex(&this.super_);
Panel_remove(&mut this.super_, rm);
let sel = Panel_getSelectedIndex(&this.super_);
rebuildSettingsArray(this, sel);
}
this.renamingNewItem = false;
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.renamingItem = None;
this.renamingNewItem = false;
this.super_.cursorOn = false;
Panel_setSelectionColor(&mut this.super_, ColorElements::PANEL_SELECTION_FOCUS);
Panel_setDefaultBar(&mut this.super_);
ScreensPanel_update(this);
}
HandlerResult::HANDLED
}
impl ScreensPanel {
fn set_item_value(&mut self, idx: usize, value: String) {
let obj: &mut dyn Object = self.super_.items[idx].object_mut();
let any: &mut dyn core::any::Any = obj;
if let Some(item) = any.downcast_mut::<ScreenListItem>() {
item.super_.value = value;
}
}
fn item_value(&self, idx: usize) -> String {
let any: &dyn core::any::Any = self.super_.items[idx].object();
any.downcast_ref::<ScreenListItem>()
.expect("ScreensPanel row is not a ScreenListItem")
.super_
.value
.clone()
}
fn item_ssIndex(&self, idx: usize) -> usize {
let any: &dyn core::any::Any = self.super_.items[idx].object();
any.downcast_ref::<ScreenListItem>()
.expect("ScreensPanel row is not a ScreenListItem")
.ssIndex
}
fn set_item_ssIndex(&mut self, idx: usize, ssIndex: usize) {
let any: &mut dyn core::any::Any = self.super_.items[idx].object_mut();
if let Some(item) = any.downcast_mut::<ScreenListItem>() {
item.ssIndex = ssIndex;
}
}
fn set_item_moving(&mut self, idx: usize, moving: bool) {
let any: &mut dyn core::any::Any = self.super_.items[idx].object_mut();
if let Some(item) = any.downcast_mut::<ScreenListItem>() {
item.super_.moving = moving;
}
}
fn focus_ptr(&self, idx: i32) -> *const () {
if idx >= 0 && (idx as usize) < self.super_.items.len() {
self.super_.items[idx as usize].object() as *const dyn Object as *const ()
} else {
core::ptr::null()
}
}
fn screens_renamingBar() -> FunctionBar {
let mut bar = Screens_renamingBar.lock().unwrap();
if bar.is_none() {
*bar = Some(FunctionBar_new(Some(&ScreensRenamingFunctions), None, None));
}
bar.as_ref().unwrap().clone()
}
}
pub fn startRenaming(this: &mut ScreensPanel) {
if this.super_.items.is_empty() {
return;
}
let sel = Panel_getSelectedIndex(&this.super_);
if this.moving {
ScreensPanel_cancelMoving(this);
}
this.renamingItem = Some(sel as usize);
this.super_.cursorOn = true;
let name = {
let obj = Panel_get(&this.super_, sel);
let any: &dyn core::any::Any = obj;
any.downcast_ref::<ScreenListItem>()
.expect("startRenaming: panel row is not a ScreenListItem")
.super_
.value
.clone()
};
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(sel as usize, text);
Panel_setSelectionColor(&mut this.super_, ColorElements::PANEL_EDIT);
this.super_.selectedLen = LineEditor_getCursor(&this.editor);
Panel_setCursorToSelection(&mut this.super_);
this.super_.currentBar = Some(ScreensPanel::screens_renamingBar());
}
pub fn rebuildSettingsArray(this: &mut ScreensPanel, selected: i32) {
let n = Panel_size(&this.super_) as usize;
let order: Vec<usize> = (0..n).map(|i| this.item_ssIndex(i)).collect();
let settings = unsafe { &mut *this.settings };
let old = std::mem::take(&mut settings.screens);
let mut slots: Vec<Option<ScreenSettings>> = old.into_iter().map(Some).collect();
let mut new_screens = Vec::with_capacity(n);
for &oldidx in &order {
new_screens.push(
slots[oldidx]
.take()
.expect("each panel row references a distinct live screen"),
);
}
settings.screens = new_screens;
for i in 0..n {
this.set_item_ssIndex(i, i);
}
let mut selected = selected;
if selected > n as i32 - 1 {
selected = n as i32 - 1;
} else if selected < 0 {
selected = 0;
}
let settings = unsafe { &mut *this.settings };
settings.ssIndex = selected as u32;
}
pub fn addNewScreen(this: &mut ScreensPanel) {
let name = "New";
let settings = unsafe { &mut *this.settings };
let ssIndex = Settings_newScreen(
settings,
&ScreenDefaults {
name: Some(name),
columns: Some("PID Command"),
sortKey: Some("PID"),
treeSortKey: None,
},
);
let item = ScreenListItem_new(name, ssIndex);
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 ScreensPanel_eventHandlerNormal(this: &mut ScreensPanel, ch: i32) -> HandlerResult {
let oldFocus = this.focus_ptr(this.super_.prevSelected);
let mut shouldRebuildArray = false;
let mut result = HandlerResult::IGNORED;
match ch {
NEWLINE | CARRIAGE_RETURN | KEY_ENTER => {
if this.moving {
ScreensPanel_cancelMoving(this);
} else {
this.moving = true;
Panel_setSelectionColor(&mut this.super_, ColorElements::PANEL_SELECTION_FOLLOW);
if !this.super_.items.is_empty() {
let idx = Panel_getSelectedIndex(&this.super_) as usize;
this.set_item_moving(idx, true);
}
}
result = HandlerResult::HANDLED;
}
KEY_MOUSE => {
if this.moving {
ScreensPanel_cancelMoving(this);
result = HandlerResult::HANDLED;
}
}
KEY_RECLICK => {
this.renamingNewItem = false;
startRenaming(this);
result = HandlerResult::HANDLED;
}
EVENT_SET_SELECTED => {
if this.moving {
ScreensPanel_cancelMoving(this);
}
result = HandlerResult::HANDLED;
}
EVENT_PANEL_LOST_FOCUS => {
if this.moving {
ScreensPanel_cancelMoving(this);
}
result = HandlerResult::HANDLED;
}
KEY_NPAGE | KEY_PPAGE | KEY_HOME | KEY_END => {
Panel_onKey(&mut this.super_, ch);
}
KEY_F2 | KEY_CTRL_R => {
this.renamingNewItem = false;
startRenaming(this);
result = HandlerResult::HANDLED;
}
KEY_F5 | KEY_CTRL_N => {
if unsafe { &*this.settings }.dynamicScreens.is_some() {
} else {
addNewScreen(this);
this.renamingNewItem = true;
startRenaming(this);
shouldRebuildArray = true;
result = HandlerResult::HANDLED;
}
}
KEY_UP => {
if !this.moving {
Panel_onKey(&mut this.super_, ch);
} else {
Panel_moveSelectedUp(&mut this.super_);
shouldRebuildArray = true;
result = HandlerResult::HANDLED;
}
}
KEY_F7 | LBRACKET | MINUS => {
Panel_moveSelectedUp(&mut this.super_);
shouldRebuildArray = true;
result = HandlerResult::HANDLED;
}
KEY_DOWN => {
if !this.moving {
Panel_onKey(&mut this.super_, ch);
} else {
Panel_moveSelectedDown(&mut this.super_);
shouldRebuildArray = true;
result = HandlerResult::HANDLED;
}
}
KEY_F8 | RBRACKET | PLUS => {
Panel_moveSelectedDown(&mut this.super_);
shouldRebuildArray = true;
result = HandlerResult::HANDLED;
}
KEY_F9 | KEY_DC | KEY_DEL_MAC => {
if Panel_size(&this.super_) > 1 {
let sel = this.super_.selected;
Panel_remove(&mut this.super_, sel);
}
shouldRebuildArray = true;
result = HandlerResult::HANDLED;
}
_ => {
if (0..255).contains(&ch) && (ch as u8 as char).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() {
core::ptr::null()
} else {
this.focus_ptr(this.super_.selected)
};
if !newFocus.is_null() && oldFocus != newFocus {
let ss_index = this.item_ssIndex(this.super_.selected as usize);
let ss_ptr: *mut ScreenSettings = {
let s = unsafe { &mut *this.settings };
&mut s.screens[ss_index]
};
let dynamic = unsafe { &*this.settings }.screens[ss_index].dynamic.clone();
let dc_ptr = unsafe { &*this.settings }
.dynamicColumns
.expect("ScreensPanel focus change: settings->dynamicColumns is NULL");
let dc: &Hashtable = unsafe { &*dc_ptr };
ColumnsPanel_fill(unsafe { &mut *this.columns }, ss_ptr, dc);
AvailableColumnsPanel_fill(
unsafe { &mut *this.availableColumns },
dynamic.as_deref(),
Some(dc),
);
result = HandlerResult::HANDLED;
}
this.super_.prevSelected = this.super_.selected;
if shouldRebuildArray {
let sel = this.super_.selected;
rebuildSettingsArray(this, sel);
}
if result == HandlerResult::HANDLED {
ScreensPanel_update(this);
}
result
}
pub fn ScreensPanel_eventHandler(this: &mut ScreensPanel, ch: i32) -> HandlerResult {
if this.renamingItem.is_some() {
ScreensPanel_eventHandlerRenaming(this, ch)
} else {
ScreensPanel_eventHandlerNormal(this, ch)
}
}
pub fn ScreensPanel_new(settings: *mut Settings, scr: *mut ScreenManager) -> ScreensPanel {
let settings_ref = unsafe { &mut *settings };
let funcs: &[&str] = if settings_ref.dynamicScreens.is_some() {
&DynamicFunctions[..]
} else {
&ScreensFunctions[..]
};
let fuBar = FunctionBar_new(Some(funcs), None, None);
{
let mut bar = Screens_renamingBar.lock().unwrap();
if bar.is_none() {
*bar = Some(FunctionBar_new(Some(&ScreensRenamingFunctions), None, None));
}
}
let super_ = Panel_new(1, 1, 1, 1, Some(fuBar));
let columns_ht: &Hashtable = unsafe {
&*settings_ref
.dynamicColumns
.expect("ScreensPanel_new: settings->dynamicColumns is NULL")
};
let ss0: *mut ScreenSettings = &mut settings_ref.screens[0];
let changed_ptr: *mut bool = &mut settings_ref.changed;
let mut columns_box = Box::new(ColumnsPanel_new(ss0, columns_ht, changed_ptr));
let columns_ptr: *mut ColumnsPanel = &mut *columns_box;
let columns_panel_ptr: *mut Panel = &mut columns_box.super_;
let mut avail_box = Box::new(AvailableColumnsPanel_new(columns_panel_ptr, columns_ht));
let avail_ptr: *mut AvailableColumnsPanel = &mut *avail_box;
let scr_ref = unsafe { &mut *scr };
ScreenManager_add(scr_ref, columns_box, 20);
ScreenManager_add(scr_ref, avail_box, -1);
let mut this = ScreensPanel {
super_,
settings,
scr,
columns: columns_ptr,
availableColumns: avail_ptr,
editor: LineEditor::default(),
moving: false,
saved: None,
renamingItem: None,
renamingNewItem: false,
};
this.super_.cursorOn = false;
LineEditor_initWithMax(&mut this.editor, SCREEN_NAME_LEN - 1);
Panel_setHeader(&mut this.super_, "Screens");
let n = unsafe { &*settings }.screens.len();
for i in 0..n {
let name = unsafe { &*settings }.screens[i]
.heading
.clone()
.unwrap_or_default();
Panel_add(&mut this.super_, Box::new(ScreenListItem_new(&name, i)));
}
this.super_.prevSelected = this.super_.selected;
this
}
pub fn ScreensPanel_update(this: &mut ScreensPanel) {
let size = Panel_size(&this.super_) as usize;
let rows: Vec<(usize, String)> = (0..size)
.map(|i| (this.item_ssIndex(i), this.item_value(i)))
.collect();
let settings = unsafe { &mut *this.settings };
settings.changed = true;
settings.lastUpdate += 1;
let old = std::mem::take(&mut settings.screens);
let mut slots: Vec<Option<ScreenSettings>> = old.into_iter().map(Some).collect();
let mut new_screens = Vec::with_capacity(size);
for (oldidx, value) in &rows {
let mut ss = slots[*oldidx]
.take()
.expect("each panel row references a distinct live screen");
ss.heading = Some(value.clone());
new_screens.push(ss);
}
settings.screens = new_screens;
for i in 0..size {
this.set_item_ssIndex(i, i);
}
}
pub struct ScreensPanel {
pub super_: Panel,
pub settings: *mut Settings,
pub scr: *mut ScreenManager,
pub columns: *mut ColumnsPanel,
pub availableColumns: *mut AvailableColumnsPanel,
pub editor: LineEditor,
pub moving: bool,
pub saved: Option<String>,
pub renamingItem: Option<usize>,
pub renamingNewItem: bool,
}
impl PanelClass for ScreensPanel {
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 {
ScreensPanel_eventHandler(self, ev)
}
}
#[cfg(test)]
use crate::ported::panel::PanelItem;
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::crt::{KEY_ENTER, KEY_UP};
use crate::ported::hashtable::Hashtable_new;
use crate::ported::panel::Panel_new;
use crate::ported::settings::HeaderLayout;
fn row(name: &str, idx: usize) -> Box<dyn Object> {
Box::new(ScreenListItem_new(name, idx))
}
fn panel_with(names: &[&str]) -> ScreensPanel {
let mut super_ = Panel_new(1, 1, 20, 10, None);
for (i, n) in names.iter().enumerate() {
super_.items.push(PanelItem::Owned(row(n, i)));
}
super_.prevSelected = super_.selected;
ScreensPanel {
super_,
settings: core::ptr::null_mut(),
scr: core::ptr::null_mut(),
columns: core::ptr::null_mut(),
availableColumns: core::ptr::null_mut(),
editor: LineEditor::default(),
moving: false,
saved: None,
renamingItem: None,
renamingNewItem: false,
}
}
fn make_settings(names: &[&str]) -> Box<Settings> {
let screens = names
.iter()
.map(|n| ScreenSettings {
heading: Some((*n).to_string()),
..Default::default()
})
.collect();
Box::new(Settings {
hLayout: HeaderLayout::HF_ONE_100,
hColumns: Vec::new(),
screens,
ssIndex: 0,
changed: false,
lastUpdate: 0,
..Default::default()
})
}
fn wired(names: &[&str]) -> (Box<Settings>, ScreensPanel) {
let mut settings = make_settings(names);
let ptr: *mut Settings = settings.as_mut();
let mut super_ = Panel_new(1, 1, 20, 10, None);
for (i, n) in names.iter().enumerate() {
super_.items.push(PanelItem::Owned(row(n, i)));
}
super_.prevSelected = super_.selected;
let sp = ScreensPanel {
super_,
settings: ptr,
scr: core::ptr::null_mut(),
columns: core::ptr::null_mut(),
availableColumns: core::ptr::null_mut(),
editor: LineEditor::default(),
moving: false,
saved: None,
renamingItem: None,
renamingNewItem: false,
};
(settings, sp)
}
fn value_at(p: &ScreensPanel, idx: usize) -> String {
let any: &dyn core::any::Any = p.super_.items[idx].object();
any.downcast_ref::<ScreenListItem>()
.unwrap()
.super_
.value
.clone()
}
fn ss_index_at(p: &ScreensPanel, idx: usize) -> usize {
let any: &dyn core::any::Any = p.super_.items[idx].object();
any.downcast_ref::<ScreenListItem>().unwrap().ssIndex
}
fn headings(s: &Settings) -> Vec<String> {
s.screens
.iter()
.map(|ss| ss.heading.clone().unwrap_or_default())
.collect()
}
fn set_moving(p: &mut ScreensPanel, idx: usize, moving: bool) {
let any: &mut dyn core::any::Any = p.super_.items[idx].object_mut();
any.downcast_mut::<ScreenListItem>().unwrap().super_.moving = moving;
}
#[test]
fn screen_list_item_new_inits_row_and_stores_ss() {
let it = ScreenListItem_new("Main", 3);
assert_eq!(it.super_.value, "Main");
assert_eq!(it.super_.key, 0); assert!(!it.super_.moving);
assert_eq!(it.ssIndex, 3); }
#[test]
fn cancel_moving_clears_all_flags_and_restores_color() {
let mut p = panel_with(&["a", "b", "c"]);
p.moving = true;
set_moving(&mut p, 0, true);
set_moving(&mut p, 2, true);
Panel_setSelectionColor(&mut p.super_, ColorElements::PANEL_SELECTION_FOLLOW);
ScreensPanel_cancelMoving(&mut p);
assert!(!p.moving);
for i in 0..3 {
let any: &dyn core::any::Any = p.super_.items[i].object();
assert!(!any.downcast_ref::<ScreenListItem>().unwrap().super_.moving);
}
assert_eq!(
p.super_.selectionColorId,
ColorElements::PANEL_SELECTION_FOCUS
);
}
#[test]
fn start_renaming_enters_edit_state() {
let mut p = panel_with(&["Main", "IO"]);
p.super_.selected = 1;
startRenaming(&mut p);
assert_eq!(p.renamingItem, Some(1));
assert_eq!(p.saved.as_deref(), Some("IO"));
assert!(p.super_.cursorOn);
assert_eq!(p.super_.selectionColorId, ColorElements::PANEL_EDIT);
assert_eq!(LineEditor_getText(&p.editor), "IO");
assert_eq!(value_at(&p, 1), "IO");
assert_eq!(p.super_.selectedLen, 2); assert!(p.super_.currentBar.is_some());
}
#[test]
fn start_renaming_cancels_in_progress_move() {
let mut p = panel_with(&["Main"]);
p.moving = true;
set_moving(&mut p, 0, true);
startRenaming(&mut p);
assert!(!p.moving);
let any: &dyn core::any::Any = p.super_.items[0].object();
assert!(!any.downcast_ref::<ScreenListItem>().unwrap().super_.moving);
assert_eq!(p.renamingItem, Some(0));
}
#[test]
fn start_renaming_empty_panel_is_noop() {
let mut p = panel_with(&[]);
startRenaming(&mut p);
assert_eq!(p.renamingItem, None);
assert!(!p.super_.cursorOn);
}
#[test]
fn renaming_default_key_edits_editor_and_row_value() {
let mut p = panel_with(&["Main"]);
startRenaming(&mut p); let r = ScreensPanel_eventHandlerRenaming(&mut p, b'X' as i32);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(LineEditor_getText(&p.editor), "MainX");
assert_eq!(value_at(&p, 0), "MainX"); assert_eq!(p.super_.selectedLen, 5); }
#[test]
fn renaming_equals_is_swallowed_without_editing() {
let mut p = panel_with(&["Main"]);
startRenaming(&mut p);
let r = ScreensPanel_eventHandlerRenaming(&mut p, EQUALS);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(LineEditor_getText(&p.editor), "Main");
assert_eq!(value_at(&p, 0), "Main");
}
#[test]
fn renaming_esc_cancels_and_restores_original_name() {
let mut p = panel_with(&["Main"]);
startRenaming(&mut p);
ScreensPanel_eventHandlerRenaming(&mut p, b'Z' as i32);
assert_eq!(value_at(&p, 0), "MainZ");
let r = ScreensPanel_eventHandlerRenaming(&mut p, ESC);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(value_at(&p, 0), "Main"); assert_eq!(p.renamingItem, None);
assert!(!p.super_.cursorOn);
assert!(!p.renamingNewItem);
assert_eq!(
p.super_.selectionColorId,
ColorElements::PANEL_SELECTION_FOCUS
);
}
#[test]
fn renaming_event_set_selected_same_row_does_not_finish() {
let mut p = panel_with(&["Main", "IO"]);
p.super_.selected = 0;
startRenaming(&mut p); let r = ScreensPanel_eventHandlerRenaming(&mut p, EVENT_SET_SELECTED);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(p.renamingItem, Some(0)); }
#[test]
fn dispatch_routes_to_normal_when_not_renaming() {
let mut p = panel_with(&["Main"]);
let r = ScreensPanel_eventHandler(&mut p, KEY_UP);
assert_eq!(r, HandlerResult::IGNORED);
}
#[test]
fn dispatch_routes_to_renaming_when_renaming() {
let mut p = panel_with(&["Main"]);
startRenaming(&mut p);
let r = ScreensPanel_eventHandler(&mut p, b'!' as i32);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(LineEditor_getText(&p.editor), "Main!");
}
#[test]
fn renaming_enter_finish_syncs_settings() {
let (settings, mut p) = wired(&["Main"]);
startRenaming(&mut p);
ScreensPanel_eventHandlerRenaming(&mut p, b'X' as i32); let r = ScreensPanel_eventHandlerRenaming(&mut p, KEY_ENTER);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(value_at(&p, 0), "MainX");
assert_eq!(p.renamingItem, None);
assert!(!p.super_.cursorOn);
assert!(settings.changed);
assert_eq!(settings.lastUpdate, 1);
assert_eq!(headings(&settings), vec!["MainX"]);
}
#[test]
fn rebuild_reorders_screens_to_panel_order() {
let (settings, mut p) = wired(&["A", "B", "C"]);
p.super_.items.swap(0, 1);
rebuildSettingsArray(&mut p, 0);
assert_eq!(headings(&settings), vec!["B", "A", "C"]);
assert_eq!(ss_index_at(&p, 0), 0);
assert_eq!(ss_index_at(&p, 1), 1);
assert_eq!(ss_index_at(&p, 2), 2);
assert_eq!(settings.ssIndex, 0);
}
#[test]
fn rebuild_clamps_selection_into_range() {
let (settings_hi, mut p_hi) = wired(&["A", "B", "C"]);
rebuildSettingsArray(&mut p_hi, 9); assert_eq!(settings_hi.ssIndex, 2);
let (settings_lo, mut p_lo) = wired(&["A", "B"]);
rebuildSettingsArray(&mut p_lo, -3); assert_eq!(settings_lo.ssIndex, 0);
}
#[test]
fn update_writes_headings_and_marks_changed() {
let (settings, mut p) = wired(&["A", "B"]);
p.set_item_value(0, "Alpha".to_string());
ScreensPanel_update(&mut p);
assert!(settings.changed);
assert_eq!(settings.lastUpdate, 1);
assert_eq!(headings(&settings), vec!["Alpha", "B"]);
}
#[test]
fn update_reorders_screens_to_panel_order() {
let (settings, mut p) = wired(&["A", "B", "C"]);
p.super_.items.swap(0, 2);
ScreensPanel_update(&mut p);
assert_eq!(headings(&settings), vec!["C", "B", "A"]);
assert_eq!(ss_index_at(&p, 0), 0);
assert_eq!(ss_index_at(&p, 2), 2);
}
#[test]
fn normal_enter_toggles_move_mode_and_updates() {
let (settings, mut p) = wired(&["Main", "IO"]);
let r = ScreensPanel_eventHandlerNormal(&mut p, KEY_ENTER);
assert_eq!(r, HandlerResult::HANDLED);
assert!(p.moving);
assert_eq!(
p.super_.selectionColorId,
ColorElements::PANEL_SELECTION_FOLLOW
);
let any: &dyn core::any::Any = p.super_.items[0].object();
assert!(any.downcast_ref::<ScreenListItem>().unwrap().super_.moving);
assert!(settings.changed);
}
#[test]
fn normal_move_down_reorders_rebuilds_and_updates() {
let (settings, mut p) = wired(&["A", "B", "C"]);
let r = ScreensPanel_eventHandlerNormal(&mut p, KEY_F8);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(value_at(&p, 0), "B");
assert_eq!(value_at(&p, 1), "A");
assert_eq!(p.super_.selected, 1);
assert_eq!(headings(&settings), vec!["B", "A", "C"]);
assert_eq!(settings.ssIndex, 1);
}
fn full(names: &[&str]) -> (Box<Settings>, Box<Hashtable>, Box<ScreenManager>, ScreensPanel) {
let mut dyncols = Box::new(Hashtable_new(8, false));
let mut settings = make_settings(names);
settings.dynamicColumns = Some(dyncols.as_mut() as *mut Hashtable);
let mut scr = Box::new(ScreenManager {
x1: 0,
y1: 0,
x2: 0,
y2: -1,
allowFocusChange: true,
panelCount: 0,
panels: Vec::new(),
name: None,
header: None,
host: None,
state: Some(crate::ported::action::State {
host: core::ptr::null_mut(),
mainPanel: core::ptr::null_mut(),
header: core::ptr::null_mut(),
failedUpdate: None,
pauseUpdate: false,
hideSelection: false,
hideMeters: false,
}),
});
let sp = ScreensPanel_new(
settings.as_mut() as *mut Settings,
scr.as_mut() as *mut ScreenManager,
);
(settings, dyncols, scr, sp)
}
#[test]
fn screens_panel_new_seeds_rows_and_wires_subpanels() {
let (_s, _hc, scr, p) = full(&["A", "B"]);
assert_eq!(Panel_size(&p.super_), 2);
assert_eq!(value_at(&p, 0), "A");
assert_eq!(value_at(&p, 1), "B");
assert_eq!(scr.panels.len(), 2);
assert!(!p.columns.is_null());
assert!(!p.availableColumns.is_null());
assert_eq!(p.super_.prevSelected, p.super_.selected);
assert!(p.super_.defaultBar.is_some());
}
#[test]
fn normal_delete_refills_columns_and_handles() {
let (_s, _hc, _scr, mut p) = full(&["A", "B", "C"]);
p.super_.selected = 1;
p.super_.prevSelected = 1;
let r = ScreensPanel_eventHandlerNormal(&mut p, KEY_F9);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(Panel_size(&p.super_), 2); }
#[test]
fn normal_f5_new_screen_adds_row_and_handles() {
let (_s, _hc, _scr, mut p) = full(&["Main"]);
let r = ScreensPanel_eventHandlerNormal(&mut p, KEY_F5);
assert_eq!(r, HandlerResult::HANDLED);
assert_eq!(Panel_size(&p.super_), 2); assert!(p.renamingNewItem); assert_eq!(value_at(&p, 1), "New");
}
}