This commit is contained in:
pdxlocations
2025-04-16 12:00:59 -07:00
parent b59ae94e00
commit e3f85ffaf2
4 changed files with 264 additions and 134 deletions
+86 -53
View File
@@ -14,6 +14,7 @@ from contact.ui.dialog import dialog
from contact.utilities.control_utils import parse_ini_file, transform_menu_path
from contact.ui.user_config import json_editor
from contact.ui.ui_state import MenuState
from contact.ui.navigation_utils import move_highlight
menu_state = MenuState()
@@ -277,62 +278,62 @@ def get_wrapped_help_text(
return wrapped_help
def move_highlight(
old_idx: int,
options: list[str],
menu_win: object,
menu_pad: object,
help_win: object,
help_text: dict[str, str],
max_help_lines: int,
menu_state: MenuState
) -> None:
# def move_highlight(
# old_idx: int,
# options: list[str],
# menu_win: object,
# menu_pad: object,
# help_win: object,
# help_text: dict[str, str],
# max_help_lines: int,
# menu_state: MenuState
# ) -> None:
if old_idx == menu_state.selected_index: # No-op
return
# if old_idx == menu_state.selected_index: # No-op
# return
max_index = len(options) + (1 if menu_state.show_save_option else 0) - 1
visible_height = menu_win.getmaxyx()[0] - 5 - (2 if menu_state.show_save_option else 0)
# max_index = len(options) + (1 if menu_state.show_save_option else 0) - 1
# visible_height = menu_win.getmaxyx()[0] - 5 - (2 if menu_state.show_save_option else 0)
# Adjust menu_state.start_index only when moving out of visible range
if menu_state.selected_index == max_index and menu_state.show_save_option:
pass
elif menu_state.selected_index < menu_state.start_index[-1]: # Moving above the visible area
menu_state.start_index[-1] = menu_state.selected_index
elif menu_state.selected_index >= menu_state.start_index[-1] + visible_height: # Moving below the visible area
menu_state.start_index[-1] = menu_state.selected_index - visible_height
pass
# # Adjust menu_state.start_index only when moving out of visible range
# if menu_state.selected_index == max_index and menu_state.show_save_option:
# pass
# elif menu_state.selected_index < menu_state.start_index[-1]: # Moving above the visible area
# menu_state.start_index[-1] = menu_state.selected_index
# elif menu_state.selected_index >= menu_state.start_index[-1] + visible_height: # Moving below the visible area
# menu_state.start_index[-1] = menu_state.selected_index - visible_height
# pass
# Ensure menu_state.start_index is within bounds
menu_state.start_index[-1] = max(0, min(menu_state.start_index[-1], max_index - visible_height + 1))
# # Ensure menu_state.start_index is within bounds
# menu_state.start_index[-1] = max(0, min(menu_state.start_index[-1], max_index - visible_height + 1))
# Clear old selection
if menu_state.show_save_option and old_idx == max_index:
menu_win.chgat(menu_win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save"))
else:
menu_pad.chgat(old_idx, 0, menu_pad.getmaxyx()[1], get_color("settings_sensitive") if options[old_idx] in sensitive_settings else get_color("settings_default"))
# # Clear old selection
# if menu_state.show_save_option and old_idx == max_index:
# menu_win.chgat(menu_win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save"))
# else:
# menu_pad.chgat(old_idx, 0, menu_pad.getmaxyx()[1], get_color("settings_sensitive") if options[old_idx] in sensitive_settings else get_color("settings_default"))
# Highlight new selection
if menu_state.show_save_option and menu_state.selected_index == max_index:
menu_win.chgat(menu_win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save", reverse=True))
else:
menu_pad.chgat(menu_state.selected_index, 0, menu_pad.getmaxyx()[1], get_color("settings_sensitive", reverse=True) if options[menu_state.selected_index] in sensitive_settings else get_color("settings_default", reverse=True))
# # Highlight new selection
# if menu_state.show_save_option and menu_state.selected_index == max_index:
# menu_win.chgat(menu_win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save", reverse=True))
# else:
# menu_pad.chgat(menu_state.selected_index, 0, menu_pad.getmaxyx()[1], get_color("settings_sensitive", reverse=True) if options[menu_state.selected_index] in sensitive_settings else get_color("settings_default", reverse=True))
menu_win.refresh()
# menu_win.refresh()
# Refresh pad only if scrolling is needed
menu_pad.refresh(menu_state.start_index[-1], 0,
menu_win.getbegyx()[0] + 3, menu_win.getbegyx()[1] + 4,
menu_win.getbegyx()[0] + 3 + visible_height,
menu_win.getbegyx()[1] + menu_win.getmaxyx()[1] - 4)
# # Refresh pad only if scrolling is needed
# menu_pad.refresh(menu_state.start_index[-1], 0,
# menu_win.getbegyx()[0] + 3, menu_win.getbegyx()[1] + 4,
# menu_win.getbegyx()[0] + 3 + visible_height,
# menu_win.getbegyx()[1] + menu_win.getmaxyx()[1] - 4)
# Update help window
transformed_path = transform_menu_path(menu_state.menu_path)
selected_option = options[menu_state.selected_index] if menu_state.selected_index < len(options) else None
help_y = menu_win.getbegyx()[0] + menu_win.getmaxyx()[0]
help_win = update_help_window(help_win, help_text, transformed_path, selected_option, max_help_lines, width, help_y, menu_win.getbegyx()[1])
# # Update help window
# transformed_path = transform_menu_path(menu_state.menu_path)
# selected_option = options[menu_state.selected_index] if menu_state.selected_index < len(options) else None
# help_y = menu_win.getbegyx()[0] + menu_win.getmaxyx()[0]
# help_win = update_help_window(help_win, help_text, transformed_path, selected_option, max_help_lines, width, help_y, menu_win.getbegyx()[1])
draw_arrows(menu_win, visible_height, max_index, menu_state)
# draw_arrows(menu_win, visible_height, max_index, menu_state)
def draw_arrows(
@@ -394,14 +395,36 @@ def settings_menu(stdscr: object, interface: object) -> None:
# max_help_lines = 4
if key == curses.KEY_UP:
old_selected_index = menu_state.selected_index
old_idx = menu_state.selected_index
menu_state.selected_index = max_index if menu_state.selected_index == 0 else menu_state.selected_index - 1
move_highlight(old_selected_index, options, menu_win, menu_pad, help_win, help_text, max_help_lines, menu_state)
move_highlight(
old_idx, menu_state.selected_index, options, menu_win, menu_pad,
start_index_ref=menu_state.start_index[-1:],
selected_index=menu_state.selected_index,
show_save=menu_state.show_save_option,
help_win=help_win,
help_updater=update_help_window,
field_mapping=help_text,
menu_path=transform_menu_path(menu_state.menu_path),
max_help_lines=max_help_lines,
sensitive_mode=True
)
elif key == curses.KEY_DOWN:
old_selected_index = menu_state.selected_index
old_idx = menu_state.selected_index
menu_state.selected_index = 0 if menu_state.selected_index == max_index else menu_state.selected_index + 1
move_highlight(old_selected_index, options, menu_win, menu_pad, help_win, help_text, max_help_lines, menu_state)
move_highlight(
old_idx, menu_state.selected_index, options, menu_win, menu_pad,
start_index_ref=menu_state.start_index[-1:],
selected_index=menu_state.selected_index,
show_save=menu_state.show_save_option,
help_win=help_win,
help_updater=update_help_window,
field_mapping=help_text,
menu_path=transform_menu_path(menu_state.menu_path),
max_help_lines=max_help_lines,
sensitive_mode=True
)
elif key == curses.KEY_RESIZE:
need_redraw = True
@@ -414,10 +437,20 @@ def settings_menu(stdscr: object, interface: object) -> None:
help_win.refresh()
elif key == ord("\t") and menu_state.show_save_option:
old_selected_index = menu_state.selected_index
old_idx = menu_state.selected_index
menu_state.selected_index = max_index
move_highlight(old_selected_index, options, menu_win, menu_pad, help_win, help_text, max_help_lines, menu_state)
move_highlight(
old_idx, menu_state.selected_index, options, menu_win, menu_pad,
start_index_ref=menu_state.start_index[-1:],
selected_index=menu_state.selected_index,
show_save=menu_state.show_save_option,
help_win=help_win,
help_updater=update_help_window,
field_mapping=help_text,
menu_path=transform_menu_path(menu_state.menu_path),
max_help_lines=max_help_lines,
sensitive_mode=True
)
elif key == curses.KEY_RIGHT or key == ord('\n'):
need_redraw = True
menu_state.start_index.append(0)
+67
View File
@@ -0,0 +1,67 @@
# contact/ui/navigation_utils.py
import curses
from contact.ui.colors import get_color
from contact.ui.user_config import save_option, sensitive_settings
def move_highlight(
old_idx: int,
new_idx: int,
options: list[str],
win: curses.window,
pad: curses.window,
*,
start_index_ref: list[int] = None,
selected_index: int = None,
max_help_lines: int = 0,
show_save: bool = False,
help_win: curses.window = None,
help_updater=None,
field_mapping=None,
menu_path: list[str] = None,
width: int = 80,
sensitive_mode: bool = False
):
if old_idx == new_idx:
return
max_index = len(options) + (1 if show_save else 0) - 1
visible_height = win.getmaxyx()[0] - 5 - (2 if show_save else 0)
# Scrolling logic
if start_index_ref is not None:
if new_idx == max_index and show_save:
pass
elif new_idx < start_index_ref[0]:
start_index_ref[0] = new_idx
elif new_idx >= start_index_ref[0] + visible_height:
start_index_ref[0] = new_idx - visible_height
start_index_ref[0] = max(0, min(start_index_ref[0], max_index - visible_height + 1))
scroll = start_index_ref[0] if start_index_ref else 0
# Clear previous highlight
if show_save and old_idx == max_index:
win.chgat(win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save"))
else:
color = "settings_sensitive" if sensitive_mode and options[old_idx] in sensitive_settings else "settings_default"
pad.chgat(old_idx, 0, pad.getmaxyx()[1], get_color(color))
# Apply new highlight
if show_save and new_idx == max_index:
win.chgat(win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save", reverse=True))
else:
color = "settings_sensitive" if sensitive_mode and options[new_idx] in sensitive_settings else "settings_default"
pad.chgat(new_idx, 0, pad.getmaxyx()[1], get_color(color, reverse=True))
win.refresh()
pad.refresh(scroll, 0,
win.getbegyx()[0] + 3, win.getbegyx()[1] + 4,
win.getbegyx()[0] + 3 + visible_height,
win.getbegyx()[1] + win.getmaxyx()[1] - 4)
# Optional help update
if help_win and help_updater and menu_path and selected_index is not None:
selected_option = options[selected_index] if selected_index < len(options) else None
help_y = win.getbegyx()[0] + win.getmaxyx()[0]
help_updater(help_win, field_mapping, menu_path, selected_option, max_help_lines, width, help_y, win.getbegyx()[1])
+63 -44
View File
@@ -5,6 +5,7 @@ from typing import Any
from contact.ui.colors import get_color, setup_colors, COLOR_MAP
from contact.ui.default_config import format_json_single_line_arrays, loaded_config
from contact.utilities.input_handlers import get_list_input
from contact.ui.navigation_utils import move_highlight
width = 80
save_option = "Save Changes"
@@ -167,53 +168,53 @@ def display_menu(menu_state: Any) -> tuple[Any, Any, list[str]]:
return menu_win, menu_pad, options
def move_highlight(
old_idx: int,
options: list[str],
menu_win: curses.window,
menu_pad: curses.window,
menu_state: Any
) -> None:
# def move_highlight(
# old_idx: int,
# options: list[str],
# menu_win: curses.window,
# menu_pad: curses.window,
# menu_state: Any
# ) -> None:
if old_idx == menu_state.selected_index: # No-op
return
# if old_idx == menu_state.selected_index: # No-op
# return
max_index = len(options) + (1 if menu_state.show_save_option else 0) - 1
visible_height = menu_win.getmaxyx()[0] - 5 - (2 if menu_state.show_save_option else 0)
# max_index = len(options) + (1 if menu_state.show_save_option else 0) - 1
# visible_height = menu_win.getmaxyx()[0] - 5 - (2 if menu_state.show_save_option else 0)
# Adjust menu_state.start_index only when moving out of visible range
if menu_state.selected_index == max_index and menu_state.show_save_option:
pass
elif menu_state.selected_index < menu_state.start_index[-1]: # Moving above the visible area
menu_state.start_index[-1] = menu_state.selected_index
elif menu_state.selected_index >= menu_state.start_index[-1] + visible_height: # Moving below the visible area
menu_state.start_index[-1] = menu_state.selected_index - visible_height
pass
# # Adjust menu_state.start_index only when moving out of visible range
# if menu_state.selected_index == max_index and menu_state.show_save_option:
# pass
# elif menu_state.selected_index < menu_state.start_index[-1]: # Moving above the visible area
# menu_state.start_index[-1] = menu_state.selected_index
# elif menu_state.selected_index >= menu_state.start_index[-1] + visible_height: # Moving below the visible area
# menu_state.start_index[-1] = menu_state.selected_index - visible_height
# pass
# Ensure menu_state.start_index is within bounds
menu_state.start_index[-1] = max(0, min(menu_state.start_index[-1], max_index - visible_height + 1))
# # Ensure menu_state.start_index is within bounds
# menu_state.start_index[-1] = max(0, min(menu_state.start_index[-1], max_index - visible_height + 1))
# Clear old selection
if menu_state.show_save_option and old_idx == max_index:
menu_win.chgat(menu_win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save"))
else:
menu_pad.chgat(old_idx, 0, menu_pad.getmaxyx()[1], get_color("settings_sensitive") if options[old_idx] in sensitive_settings else get_color("settings_default"))
# # Clear old selection
# if menu_state.show_save_option and old_idx == max_index:
# menu_win.chgat(menu_win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save"))
# else:
# menu_pad.chgat(old_idx, 0, menu_pad.getmaxyx()[1], get_color("settings_sensitive") if options[old_idx] in sensitive_settings else get_color("settings_default"))
# Highlight new selection
if menu_state.show_save_option and menu_state.selected_index == max_index:
menu_win.chgat(menu_win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save", reverse=True))
else:
menu_pad.chgat(menu_state.selected_index, 0, menu_pad.getmaxyx()[1], get_color("settings_sensitive", reverse=True) if options[menu_state.selected_index] in sensitive_settings else get_color("settings_default", reverse=True))
# # Highlight new selection
# if menu_state.show_save_option and menu_state.selected_index == max_index:
# menu_win.chgat(menu_win.getmaxyx()[0] - 2, (width - len(save_option)) // 2, len(save_option), get_color("settings_save", reverse=True))
# else:
# menu_pad.chgat(menu_state.selected_index, 0, menu_pad.getmaxyx()[1], get_color("settings_sensitive", reverse=True) if options[menu_state.selected_index] in sensitive_settings else get_color("settings_default", reverse=True))
menu_win.refresh()
# menu_win.refresh()
# Refresh pad only if scrolling is needed
menu_pad.refresh(menu_state.start_index[-1], 0,
menu_win.getbegyx()[0] + 3, menu_win.getbegyx()[1] + 4,
menu_win.getbegyx()[0] + 3 + visible_height,
menu_win.getbegyx()[1] + menu_win.getmaxyx()[1] - 4)
# # Refresh pad only if scrolling is needed
# menu_pad.refresh(menu_state.start_index[-1], 0,
# menu_win.getbegyx()[0] + 3, menu_win.getbegyx()[1] + 4,
# menu_win.getbegyx()[0] + 3 + visible_height,
# menu_win.getbegyx()[1] + menu_win.getmaxyx()[1] - 4)
draw_arrows(menu_win, visible_height, max_index, menu_state)
# draw_arrows(menu_win, visible_height, max_index, menu_state)
def draw_arrows(
@@ -274,20 +275,38 @@ def json_editor(stdscr: curses.window, menu_state: Any) -> None:
if key == curses.KEY_UP:
old_selected_index = menu_state.selected_index
old_idx = menu_state.selected_index
menu_state.selected_index = max_index if menu_state.selected_index == 0 else menu_state.selected_index - 1
move_highlight(old_selected_index, options, menu_win, menu_pad, menu_state)
move_highlight(
old_idx, menu_state.selected_index, options, menu_win, menu_pad,
start_index_ref=menu_state.start_index[-1:],
selected_index=menu_state.selected_index,
show_save=menu_state.show_save_option,
sensitive_mode=True
)
elif key == curses.KEY_DOWN:
old_selected_index = menu_state.selected_index
old_idx = menu_state.selected_index
menu_state.selected_index = 0 if menu_state.selected_index == max_index else menu_state.selected_index + 1
move_highlight(old_selected_index, options, menu_win, menu_pad, menu_state)
move_highlight(
old_idx, menu_state.selected_index, options, menu_win, menu_pad,
start_index_ref=menu_state.start_index[-1:],
selected_index=menu_state.selected_index,
show_save=menu_state.show_save_option,
sensitive_mode=True
)
elif key == ord("\t") and menu_state.show_save_option:
old_selected_index = menu_state.selected_index
old_idx = menu_state.selected_index
menu_state.selected_index = max_index
move_highlight(old_selected_index, options, menu_win, menu_pad, menu_state)
move_highlight(
old_idx, menu_state.selected_index, options, menu_win, menu_pad,
start_index_ref=menu_state.start_index[-1:],
selected_index=menu_state.selected_index,
show_save=menu_state.show_save_option,
sensitive_mode=True
)
elif key in (curses.KEY_RIGHT, 10, 13): # 10 = \n, 13 = carriage return
+48 -37
View File
@@ -5,6 +5,7 @@ import ipaddress
import re
from typing import Any, Optional
from contact.ui.colors import get_color
from contact.ui.navigation_utils import move_highlight
def wrap_text(text: str, wrap_width: int) -> list[str]:
"""Wraps text while preserving spaces and breaking long words."""
@@ -383,13 +384,23 @@ def get_list_input(prompt: str, current_option: Optional[str], list_options: lis
key = list_win.getch()
if key == curses.KEY_UP:
old_selected_index = selected_index
old_idx = selected_index
selected_index = max(0, selected_index - 1)
move_highlight(old_selected_index, selected_index, list_options, list_win, list_pad)
scroll_ref = [scroll_offset]
move_highlight(
old_idx, selected_index, list_options, list_win, list_pad,
start_index_ref=scroll_ref
)
scroll_offset = scroll_ref[0]
elif key == curses.KEY_DOWN:
old_selected_index = selected_index
old_idx = selected_index
selected_index = min(len(list_options) - 1, selected_index + 1)
move_highlight(old_selected_index, selected_index, list_options, list_win, list_pad)
scroll_ref = [scroll_offset]
move_highlight(
old_idx, selected_index, list_options, list_win, list_pad,
start_index_ref=scroll_ref
)
scroll_offset = scroll_ref[0]
elif key == ord('\n'): # Enter key
list_win.clear()
list_win.refresh()
@@ -400,50 +411,50 @@ def get_list_input(prompt: str, current_option: Optional[str], list_options: lis
return current_option
def move_highlight(
old_idx: int,
new_idx: int,
options: list[str],
list_win: curses.window,
list_pad: curses.window
) -> int:
# def move_highlight(
# old_idx: int,
# new_idx: int,
# options: list[str],
# list_win: curses.window,
# list_pad: curses.window
# ) -> int:
global scroll_offset
if 'scroll_offset' not in globals():
scroll_offset = 0 # Initialize if not set
# global scroll_offset
# if 'scroll_offset' not in globals():
# scroll_offset = 0 # Initialize if not set
if old_idx == new_idx:
return # No-op
# if old_idx == new_idx:
# return # No-op
max_index = len(options) - 1
visible_height = list_win.getmaxyx()[0] - 5
# max_index = len(options) - 1
# visible_height = list_win.getmaxyx()[0] - 5
# Adjust scroll_offset only when moving out of visible range
if new_idx < scroll_offset: # Moving above the visible area
scroll_offset = new_idx
elif new_idx >= scroll_offset + visible_height: # Moving below the visible area
scroll_offset = new_idx - visible_height
# # Adjust scroll_offset only when moving out of visible range
# if new_idx < scroll_offset: # Moving above the visible area
# scroll_offset = new_idx
# elif new_idx >= scroll_offset + visible_height: # Moving below the visible area
# scroll_offset = new_idx - visible_height
# Ensure scroll_offset is within bounds
scroll_offset = max(0, min(scroll_offset, max_index - visible_height + 1))
# # Ensure scroll_offset is within bounds
# scroll_offset = max(0, min(scroll_offset, max_index - visible_height + 1))
# Clear old highlight
list_pad.chgat(old_idx, 0, list_pad.getmaxyx()[1], get_color("settings_default"))
# # Clear old highlight
# list_pad.chgat(old_idx, 0, list_pad.getmaxyx()[1], get_color("settings_default"))
# Highlight new selection
list_pad.chgat(new_idx, 0, list_pad.getmaxyx()[1], get_color("settings_default", reverse=True))
# # Highlight new selection
# list_pad.chgat(new_idx, 0, list_pad.getmaxyx()[1], get_color("settings_default", reverse=True))
list_win.refresh()
# list_win.refresh()
# Refresh pad only if scrolling is needed
list_pad.refresh(scroll_offset, 0,
list_win.getbegyx()[0] + 3, list_win.getbegyx()[1] + 4,
list_win.getbegyx()[0] + 3 + visible_height,
list_win.getbegyx()[1] + list_win.getmaxyx()[1] - 4)
# # Refresh pad only if scrolling is needed
# list_pad.refresh(scroll_offset, 0,
# list_win.getbegyx()[0] + 3, list_win.getbegyx()[1] + 4,
# list_win.getbegyx()[0] + 3 + visible_height,
# list_win.getbegyx()[1] + list_win.getmaxyx()[1] - 4)
draw_arrows(list_win, visible_height, max_index, scroll_offset)
# draw_arrows(list_win, visible_height, max_index, scroll_offset)
return scroll_offset # Return updated scroll_offset to be stored externally
# return scroll_offset # Return updated scroll_offset to be stored externally
def draw_arrows(