Skip to content

feat: ModifiedGutter pattern for property editors#6697

Draft
vlsi wants to merge 8 commits intoapache:masterfrom
vlsi:gutter-pattern
Draft

feat: ModifiedGutter pattern for property editors#6697
vlsi wants to merge 8 commits intoapache:masterfrom
vlsi:gutter-pattern

Conversation

@vlsi
Copy link
Copy Markdown
Collaborator

@vlsi vlsi commented May 7, 2026

Summary

Adds a "modified gutter" — a thin coloured strip on the left edge of property
editors that lights up when the field's value is explicitly stored on the test
element. Mirrors the IntelliJ IDEA / VS Code convention so users can tell at a
glance which fields they have overridden vs. which still inherit a default.

The series introduces:

  • ModifiedGutter widget + UnsetMode / ExpressionMode / ResetMode sealed types.
  • New gutter-aware editors: JBooleanPropertyEditor, JEnumPropertyEditor,
    JStringPropertyEditor (single-line) and JEditableTextArea (multi-line).
  • Right-click "Reset to default" in the popup menu (kbd: extra Backspace on an
    empty modified text field).
  • Symmetric updateUi / updateElement so a save / reload round-trip preserves
    explicit values that happen to equal the default.
  • User-manual entry under "Configuring Tree Elements".

Test plan

  • :src:jorphan:test and :src:core:test are green
  • Smoke-test in the GUI: HTTP Request → Advanced → toggle "Retrieve all
    embedded resources", "URLs must match", "URLs must not match"; Comment
    field on any element. Save / reload a .jmx and confirm explicit values
    are preserved.

🤖 Generated with Claude Code

vlsi and others added 8 commits May 7, 2026 19:38
Adds two related improvements to JEditableCheckBox:

* The check-box label, true/false captions, and the "Use Expression"
  menu item now go through a ResourceLocalizer so they can be
  translated from JMeter resource bundles.
* A small ellipsis button (and a matching popup menu item) lets the
  user switch the editor to expression mode, where a free-form text
  field accepts ${variable} / ${__P(...)} expressions instead of a
  plain boolean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…xpressionMode

Adds a new pair of Kotlin GUI components — JEnumPropertyEditor (for
enum-typed properties) and the underlying JEditableComboBox — together
with the supporting infrastructure for binding them to a TestElement.

The configuration uses two sealed types:

* `UnsetMode` — `Forbid` or `Allow(unsetValue)` — controls whether the
  combobox lets the user clear the selection. The unset entry is shown
  in italics and the editor reports `null` when it is selected.
* `ExpressionMode` — `Forbid` or `Allow(useExpression, useExpressionTooltip)` —
  controls whether the combobox can be switched to a free-form text
  field for `${variable}` / `${__P(...)}` expressions.

JEnumPropertyEditor exposes a `JEnumPropertyEditor.create(...)` factory
that picks sensible defaults for `unsetMode` / `expressionMode`, plus a
data-class `Configuration` for callers that need full control.

Refactors EnumEditor / ConstantThroughputTimer / CSVDataSet so that
the schema-based property descriptors are consistent with the new
editor, and tightens GenericTestBeanCustomizer accordingly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a left-side accent strip (ModifiedGutter) that lights up next to a
property editor when its value is explicitly stored on the test
element. Follows the gutter pattern used by IntelliJ IDEA and VS Code,
keeping configuration forms visually quiet.

Key changes:

* New ModifiedGutter wrapper panel with a LaF-aware accent colour
  (muted when the wrapped control is disabled) and a public
  isModified property that fires PropertyChangeEvents. The gutter
  slot is reserved even when transparent, so toggling the state never
  causes a layout shift.
* JEditableCheckBox / JEditableComboBox grow a public isModified and
  a protected resetToDefault hook; their root layout becomes
  BorderLayout (gutter on the left), the original CardLayout moves
  into an inner cardPanel.
* New ResetMode sealed type wires a "Reset to default" entry into the
  popup menu of the editors. The action is enabled only while the
  gutter is lit, and the popup is reachable both from the regular
  card and from the expression card.
* Explicit-set semantics: JBooleanPropertyEditor / JEnumPropertyEditor
  use a suppress flag so any user-driven value change marks the
  editor modified (and stays modified until reset, even if the user
  happens to land back on the default value). Loading from a
  TestElement marks the editor modified iff the property is actually
  stored on the element, and updateElement is gated on isModified —
  so the gutter, the test element and a save/reload round-trip all
  agree even when the explicit value happens to equal the default.
* Tests:
  * ModifiedGutterTest covers layout reservation, property-change
    semantics, and accessibility description.
  * JEditableCheckBoxModifiedTest guards against regressions in the
    value getter/setter after the layout refactor.
  * JEditableCheckBoxGutterSemanticsTest exercises the full state
    machine via a test-double subclass that mirrors the production
    pattern.
  * JBooleanPropertyEditorTest and JEnumPropertyEditorTest exercise
    the full updateElement → updateUi cycle through a real
    AbstractTestElement, including round-trip preservation of
    explicit values that equal the default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-as-reset

Adds a third gutter-aware property editor for plain string properties,
together with a backspace gesture that resets the field to its default
when invoked on an already-empty modified field.

Key changes:

* New JEditableTextField in jorphan.gui — a gutter-wrapped JTextField
  with an optional Reset action in the popup menu. There is no
  separate expression card: free-form text already accepts
  ${expressions}, so a card switch would just add chrome.
* "Quick reset" via Backspace / Delete: pressing the delete key on
  an empty modified field is interpreted as "undo my custom value",
  saving a popup trip. The first backspace clears the last character;
  the next backspace on the now-empty field resets to default and
  clears the gutter.
* New JStringPropertyEditor in core.gui — binds JEditableTextField to
  a StringPropertyDescriptor with the same explicit-set semantics as
  JBooleanPropertyEditor / JEnumPropertyEditor (suppress flag,
  value-change listener, updateUi marks the editor modified iff the
  property is stored on the element). Persistence is symmetric:
  updateElement removes the property when not modified and stores the
  value verbatim when modified, so explicit empty strings now survive
  a save/reload round-trip.
* HttpTestSampleGui: embeddedAllowRE / embeddedExcludeRE are now
  JStringPropertyEditor instances and provide the first smoke-test of
  the gutter pattern on text inputs. The MigLayout column constraints
  on the surrounding panel are tightened so that the two URL labels
  share column 0 and their fields line up under the wider checkbox
  row above (the original switch dragged the labels far from their
  fields).
* JEditableTextFieldGutterSemanticsTest covers the full state machine,
  the popup menu wiring, and the backspace / delete gestures.
* JStringPropertyEditorTest exercises updateElement → updateUi through
  a real AbstractTestElement, including the round-trip case for an
  explicit empty string.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a multi-line counterpart to JEditableTextField — same gutter,
same Reset action, same backspace-as-reset gesture (triggered only
when the entire text area is empty).

* New JEditableTextArea in jorphan.gui. The text area is added
  directly to the gutter without a JScrollPane so that short
  comment-style fields render naturally; callers that need scrolling
  can wrap the editor or its inner JTextArea externally.

* AbstractJMeterGuiComponent.commentField is now backed by a
  JEditableTextArea. The legacy `commentField` JTextArea reference is
  retained and points at the editor's inner text area so existing
  setText / getText callers keep working unchanged. The editor uses a
  simple "non-empty == modified" rule rather than the explicit-set
  semantics from JStringPropertyEditor — comments have no notion of
  "default vs absent", just "set vs cleared", so the listener
  recomputes the modified flag from the live text on every value
  change.

* New JEditableTextAreaGutterSemanticsTest covers the same scenarios
  as the text-field test plus a check that multi-line content is
  read back verbatim and that backspace inside multi-line text falls
  through to the standard JTextArea behaviour (16 scenarios).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "Use Expression" action was previously surfaced both as an entry in
the popup menu and as a small ellipsis button glued to the right of the
checkbox / combobox. After "Reset to default" was added to the popup,
the gutter became the primary visual indicator, and free-form text
fields already work without an ellipsis button — keeping the persistent
button only on checkboxes and comboboxes is inconsistent and adds
visual noise to forms with many fields.

Drops the button from JEditableCheckBox and JEditableComboBox. The
Use Expression action remains reachable from the popup menu (right
click on the control) on every editor type, which is symmetric with
the Reset action and matches what JEditableTextField / JEditableTextArea
have always done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant