Skip to content

feat (display/rtplot): extract RTScaledWidgetRepresentation + add show_scale_labels to Tank#3767

Open
emilioheredia-source wants to merge 4 commits intoControlSystemStudio:masterfrom
emilioheredia-source:display/rtscaled-tank-refactor
Open

feat (display/rtplot): extract RTScaledWidgetRepresentation + add show_scale_labels to Tank#3767
emilioheredia-source wants to merge 4 commits intoControlSystemStudio:masterfrom
emilioheredia-source:display/rtscaled-tank-refactor

Conversation

@emilioheredia-source
Copy link
Copy Markdown

Summary

Refactors the Tank widget's JavaFX wiring to eliminate the duplication that
existed between TankRepresentation and (the future) RTProgressBarRepresentation,
and adds a new show_scale_labels property to TankWidget and the underlying
YAxisImpl canvas axis.

This PR depends on: tank_scale_refactor (already merged as #3760).
It is recommended to merge this after #3766
(parallel_rendering preference for RTTank), but the two PRs are independent —
each applies cleanly on top of master without the other.
Merging this PR, also sets up the base class and show_scale_labels infrastructure
needed by the RTTank-based Progress Bar refactor, which will be submitted as the next PR.


What Changed

New: RTScaledWidgetRepresentation<W> abstract base class

A generic base for all widget representations whose JFX node is an RTTank.
Handles everything that depends only on the ScaledPVWidget contract:

  • Creating and throttle-configuring the RTTank
  • Forwarding PV value and display-range changes to the tank
  • Evaluating alarm limit lines from PV metadata or widget properties
  • 90° orientation transform for horizontal layout
  • Scheduling representation updates on property changes

Subclasses provide four template methods:

Method Purpose
isHorizontal() Read the widget's own horizontal property
registerLookListeners() Add listeners on widget-specific appearance properties
unregisterLookListeners() Remove those listeners
applyLookToTank(w, h) Push current appearance to the tank

configureTank() is an optional no-op hook called once after tank creation.

Result: TankRepresentation collapses from ~270 lines to ~90 lines of
widget-specific code. RTProgressBarRepresentation (PR3) is ~110 lines.

ScaledPVWidget — new propShowScaleLabels descriptor

show_scale_labels (boolean, default true) is added to ScaledPVWidget
so both Tank and ProgressBar inherit it without duplication.

TankWidget — new show_scale_labels property

Exposes show_scale_labels in the property editor. Default true preserves
existing behaviour.

RTTanksetScaleLabelsVisible(boolean)

Delegates to both YAxisImpl instances (left and right scale). Each axis
fires requestLayout()/requestRefresh() internally via plot_part_listener
when its state changes, so no extra need_layout or requestUpdate() calls
are needed in RTTank.

YAxisImpl — ticks-only rendering mode (show_labels)

Method Behaviour when show_labels = false
getDesiredPixelSize Returns TICK_LENGTH (10 px) immediately — no label metrics computed
getPixelGaps Returns {0, 0} — rotated labels no longer overhang endpoints
paint Skips drawTickLabel and paintLabels; tick marks and axis line draw normally

show_labels is volatile because it is read on the Java2D render thread
and written on the JFX application thread.

setScaleLabelsVisible has an early-return guard to suppress spurious
relayout when the value has not actually changed.

Use case: stacked widgets sharing a single labelled scale


Backward Compatibility

  • Default show_scale_labels=true — existing .bob files load and display
    identically.
  • New XML element <show_scale_labels> is silently ignored by older Phoebus.
  • RTScaledWidgetRepresentation is package-private. No public API change.

Files Changed

RTScaledWidgetRepresentation.java | 343 +++ (new)
TankRepresentation.java | 241 +/--
RTTank.java | 102 ++-
YAxisImpl.java | 38 ++-
ScaledPVWidget.java | 36 ++-
TankWidget.java | 39 +/-
Messages.java | 2 +
messages.properties | 2 +
8 files changed, ~548 insertions, ~255 deletions


Design Decisions

Decision Rationale
show_labels as volatile The field is written on the JFX thread (setScaleLabelsVisible) and read on the Java2D render thread (paint, getDesiredPixelSize). Without volatile the write may not be visible to the render thread.
No extra requestUpdate in setScaleLabelsVisible The YAxisImpl axes call requestLayout()/requestRefresh() themselves when the value changes; these propagate to RTTank.need_layout and requestUpdate() via plot_part_listener. A second explicit call would cause a redundant double-render.
show_scale_labels in ScaledPVWidget Both Tank and ProgressBar benefit from the feature. Placing the descriptor in the base class avoids declaring it twice.
Descriptor only, not a defineProperties entry in ScaledPVWidget Each concrete widget decides whether to expose the property, allowing Tank to default true and a future Meter or Thermometer to choose differently.

…r RTTank widgets

TankRepresentation contained all RTTank lifecycle logic inline: PV value and
range forwarding, alarm limit evaluation, orientation transforms, and throttle
configuration.  Any further RTTank-backed widget (e.g. RTProgressBar) would
need every line of it duplicated.

Fix: extract RTScaledWidgetRepresentation<W extends ScaledPVWidget>, a generic
abstract base class that owns all shared behaviour:
  - RTTank creation and update-throttle configuration
  - forwarding PV value / display-range changes to the tank
  - evaluating alarm limit lines from PV metadata or widget properties
  - 90 deg orientation transform for horizontal layout
  - scheduling JFX representation updates on property changes

Performance improvement in valueChanged():
  tank.setRange() calls scale.setValueRange(), which recomputes the full tick
  layout.  This is now skipped when a pure PV value arrives and limits_from_pv
  is false, avoiding needless tick recomputation at up to 20 Hz per widget.

Subclasses implement four small abstract methods:
  isHorizontal()                  — read the widget horizontal property
  registerLookListeners()         — add listeners on widget-specific props
  unregisterLookListeners()       — matching removals
  applyLookToTank(width, height)  — push appearance props to the tank

TankRepresentation is now a ~30-line thin subclass with no duplicated logic.
No behaviour change for existing Tank widget displays.
Allow the numeric scale to render tick marks without label text.
When show_scale_labels=false, tick positions are identical to the
labelled mode, so multiple stacked Tank or ProgressBar widgets can
share a single labelled scale: only the first widget shows text;
the rest show aligned ticks only, saving space without losing the
alignment grid.

## YAxisImpl changes

  - volatile boolean show_labels (default true)
    Read on the Java2D render thread, written on the JFX application
    thread; must be volatile.
  - getDesiredPixelSize: returns TICK_LENGTH (10 px) immediately when
    show_labels=false, skipping all label metric computation.
  - getPixelGaps: returns {0,0} when show_labels=false; rotated labels
    no longer overhang past the axis endpoints.
  - paint: skips drawTickLabel and paintLabels when show_labels=false;
    tick marks and the axis line are still drawn normally.
  - setScaleLabelsVisible: guarded early-return prevents spurious
    relayout when the value has not changed.

## RTTank changes

  - setScaleLabelsVisible(boolean): delegates to both left and right
    YAxisImpl axes.  Each axis calls requestLayout()/requestRefresh()
    internally when its state changes; these propagate to RTTank's
    need_layout flag and requestUpdate() via plot_part_listener, so
    RTTank requires no additional calls.

## Model / representation changes

  ScaledPVWidget  - propShowScaleLabels descriptor (shared by Tank + ProgressBar)
  TankWidget      - show_scale_labels property, default true
  TankRepresentation  - lookListener + applyLookToTank: tank.setScaleLabelsVisible()
  Messages / messages.properties  - ShowScaleLabels, InnerPadding display strings
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 4, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants