"""Editable parameter rows per controller group.""" from textual.app import ComposeResult from textual.containers import Vertical, Horizontal from textual.widgets import Static, Input, Button from ..protocol import ParamDef, PARAMS, build_param_write class ParamRow(Horizontal): """Single parameter row with label, current value, input, and send button.""" DEFAULT_CSS = """ ParamRow { height: auto; width: 100%; padding: 0; margin: 0; } ParamRow .param-label { width: 16; height: 1; padding: 0 1; } ParamRow .param-value { width: 14; height: 1; padding: 0 1; color: $success; } ParamRow Input { width: 1fr; } ParamRow Button { width: 6; min-width: 6; } """ def __init__(self, param: ParamDef, send_callback): super().__init__() self.param = param self._send_callback = send_callback def compose(self) -> ComposeResult: yield Static(self.param.name, classes="param-label") yield Static("---", id=f"val_{self.param.id:02x}", classes="param-value") yield Input(placeholder="new", id=f"input_{self.param.id:02x}") yield Button("Go", id=f"btn_{self.param.id:02x}", variant="primary") def set_current_value(self, value: float): fmt = self.param.fmt label = self.query_one(f"#val_{self.param.id:02x}", Static) label.update(f"{value:{fmt}}") def on_button_pressed(self, event: Button.Pressed) -> None: inp = self.query_one(Input) try: value = float(inp.value) except ValueError: return frame = build_param_write(self.param.id, self.param.ptype, value) self._send_callback(frame) inp.value = "" def on_input_submitted(self, event: Input.Submitted) -> None: try: value = float(event.value) except ValueError: return frame = build_param_write(self.param.id, self.param.ptype, value) self._send_callback(frame) event.input.value = "" class ParamGroup(Vertical): """A group of parameter rows for one controller.""" DEFAULT_CSS = """ ParamGroup { width: 100%; height: auto; padding: 0 1; margin: 0 0 1 0; } """ def __init__(self, group_name: str, send_callback): super().__init__() self.group_name = group_name self._send_callback = send_callback self._params = [p for p in PARAMS if p.group == group_name] def compose(self) -> ComposeResult: yield Static(f"[bold]{self.group_name} CONTROLLER[/bold]") for p in self._params: yield ParamRow(p, self._send_callback) def update_param(self, param_id: int, value: float): for row in self.query(ParamRow): if row.param.id == param_id: row.set_current_value(value) return