Skip to content

Feature: Improve Mask Painter UI Layout and Usability #89

@yulewu

Description

@yulewu

The current Mask Painter panel has several layout and usability issues that are particularly visible in narrow side-panel contexts:

  1. Horizontal overflow in the class color list — The 30%/70% HBox split gives the sorting container only ~105px at typical panel widths, causing TagsInput chips to overflow and the "Show all classes" label to truncate to "Show all c...".
  2. ToggleButtons for outline/fill are too wide — Each row renders a ToggleButtons at 160px, causing horizontal overflow in the per-class list.
  3. Palette tabs always visible — The Save/Load/Manage Tab occupies permanent vertical space even when not in use, contributing to a cluttered, scroll-heavy panel.
  4. Output widget for feedback — Uses a console-style Output widget for status/error messages, which looks out of place and doesn't support styled text.
  5. No visual sectioning — There are no dividers between the identifier row, the color pickers, and palette management, making the panel hard to scan.
  6. "No identifier selected" warning on Save — The Save button is always enabled even before an identifier or name is set, leading to dead-end warning messages.

Proposed Changes

1. Fix the class color list layout — vertical, full-width

Replace the 30%/70% HBox split with a vertical VBox. Move TagsInput and Default color above the per-class list at full width:

VBox
├─ HBox: [Label "Class order / visible:"] [Show all □]
├─ TagsInput  (full width)
├─ HBox: [Label "Default color:"] [ColorPicker]
├─ HTML <hr>
└─ VBox (per-class rows, scrollable)
     [□ vis] [■ ColorPicker "ClassName"] [□ fill]
     ...

2. Compact outline/fill toggle

Replace ToggleButtons(options=["outline","fill"], style={"button_width":"70px"}) with a single Checkbox(description="fill", layout=Layout(width="60px")). Saves ~100px per row.

3. Wrap palette management in a collapsed Accordion

palette_accordion = Accordion(children=[color_set_tab], selected_index=None)
palette_accordion.set_title(0, "Palette: Save / Load / Manage")

Takes zero height by default; expands on demand.

4. Replace Output feedback widget with a styled HTML label

# error
feedback_label.value = '<span style="color:orange">⚠️ No identifier selected.</span>'
# success
feedback_label.value = '<span style="color:green">✓ Colors applied.</span>'

5. Two-row top bar

Row 1: [☑ Enable]  [Identifier ▾──────────────────]
Row 2: [Update Colors]  [Apply saved ▾──────]

Moving Enable to the left as the primary toggle, and consolidating Apply saved + saved_sets_dropdown into the action row reduces the need to navigate into the Manage tab for the common apply workflow.

6. Disable Save button reactively

Disable save_button until both a palette name is entered and an identifier is selected. This prevents the current dead-end warning flow.


Final Layout

VBox (max_height=600px, overflow_y=auto)
├─ HBox: [☑ Enable] [Identifier ▾──────────────]
├─ HBox: [Update Colors] [Apply: saved_sets_dropdown ▾] [Manage...]
├─ HTML <hr>
├─ HBox: [Label "Class order:"] [Show all □]
├─ TagsInput (full width)
├─ HBox: [Label "Default color:"] [■ ColorPicker]
├─ HTML <hr>
├─ VBox (scrollable per-class rows)
│    [□] [■ ColorPicker "CD4+"] [□ fill]
│    [□] [■ ColorPicker "CD8+"] [□ fill]
│    ...
├─ Accordion (collapsed): "Palette: Save / Load / Manage"
│    └─ Tab: Save | Load | Manage
└─ HTML feedback_label (inline styled)

Acceptance Criteria

  • TagsInput and Default color render at full panel width without truncation
  • Each class row fits within the panel without horizontal scroll (vis checkbox + ColorPicker + fill checkbox)
  • Palette Save/Load/Manage section is collapsed by default and expands on click
  • Status and error messages render as styled inline text (not console output)
  • Enable checkbox is the leftmost, most prominent control in the top bar
  • Save set button is disabled until both an identifier is selected and a palette name is entered
  • No regression to existing palette save/load/apply/delete behavior

Files Affected

  • ueler/viewer/plugin/mask_painter.pyUiComponent.__init__(), MaskPainterDisplay.initiate_ui(), MaskPainterDisplay.on_identifier_change(), MaskPainterDisplay._log()
  • mask_painter.py — UiComponent.__init__(), MaskPainterDisplay.initiate_ui(), MaskPainterDisplay.on_identifier_change(), MaskPainterDisplay._log()

Metadata

Metadata

Assignees

Labels

No labels
No labels
No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions