Project

General

Profile

Feature #10451

GUI interaction event logging

Added by Paula Păstrăguș 11 months ago. Updated about 2 months ago.

Status:
Test
Priority:
Normal
Target version:
-
Start date:
Due date:
% Done:

100%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
reviewer:
production:
No
env_name:
topics:

10450wip.patch Magnifier (2.63 KB) Paula Păstrăguș, 08/20/2025 09:38 AM

example.log Magnifier (5.37 KB) Paula Păstrăguș, 09/01/2025 03:31 AM

10451a.patch Magnifier (8.58 KB) Paula Păstrăguș, 09/08/2025 07:26 AM

web_gui_pmp_54066_bogus_0.log Magnifier (884 KB) Paula Păstrăguș, 09/08/2025 07:38 AM

History

#1 Updated by Paula Păstrăguș 11 months ago

This task is meant to add logging for key GUI actions so we can reconstruct user flows and reproduce "one-off" issues (e.g., crashes, OOM). When enabled (intended for testing systems, not production), the client will emit structured logs for things like window x opens/close.

In order to achieve this, we can extend an existing log file, or we can create a dedicated one, gui-actions.log to reduce confusion.

A entry can look like this: <time> <session ID> <event> <desc>
2025-08-29 000014 WINDOW_OPEN "window title".

#2 Updated by Paula Păstrăguș 11 months ago

Hynek, I’d value your feedback on this and your approval. Thank you!

#3 Updated by Greg Shah 11 months ago

A common log file is useful because then all logging has context about what was happening. To make that work, we would need to ensure that none of the extended debug logging would be present by default.

I also don't want to create a parallel logging infrastructure. Let's use what we already have.

#4 Updated by Paula Păstrăguș 11 months ago

In that case, we can stick with the existing logger levels defined in directory.xml, no need to introduce any new variables, just as you suggested, Greg. I'll review where the logs would fit best and outline what’s useful and where to place it before committing to a solution.

#5 Updated by Paula Păstrăguș 11 months ago

  • Assignee set to Paula Păstrăguș
  • Status changed from New to WIP

#6 Updated by Paula Păstrăguș 11 months ago

I’ve attached a patch that adds structured logging for key GUI events, specifically when windows are opened/closed and when buttons are clicked.

Hynek, please take a look.

The idea behind these logs is to make it easier to trace the sequence of user actions leading up to a crash or unexpected behavior. For example:

  • Window opened/closed logs help reconstruct the navigation flow (which screen the user was on, and in what order). This is especially useful if issues appear only after entering or leaving certain dialogs.
  • Button click logs capture the specific actions the user performed. When combined with the window context, they show why the application transitioned into a particular state.

With this information, we can replay the user’s journey and reproduce the bug much more reliably. It also helps to distinguish whether a failure was triggered by a particular user action, or whether it occurred 'spontaneously' in the background.

Here’s a sample of how the logs look:

25/08/20 16:16:32.920+0300 |    INFO | com.goldencode.p2j.ui.client.widget.TitledWindow | PID:81199, ThreadName:main, Session:00000017, ThreadId:00000001, User:bogus | Window opened: Display customer details
25/08/20 16:16:40.896+0300 |    INFO | com.goldencode.p2j.ui.client.gui.ButtonGuiImpl | PID:81199, ThreadName:main, Session:00000017, ThreadId:00000001, User:bogus | User clicked: Next button.
25/08/20 16:16:42.037+0300 |    INFO | com.goldencode.p2j.ui.client.gui.ButtonGuiImpl | PID:81199, ThreadName:main, Session:00000017, ThreadId:00000001, User:bogus | User clicked: Previous button.
25/08/20 16:16:43.024+0300 |    INFO | com.goldencode.p2j.ui.client.gui.ButtonGuiImpl | PID:81199, ThreadName:main, Session:00000017, ThreadId:00000001, User:bogus | User clicked: Next button.

By default, these logs are suppressed. In directory.xml, the config for the relevant classes will be set to WARNING:

            <node class="string" name="com.goldencode.p2j.ui.client.gui.ButtonGuiImpl">
              <node-attribute name="value" value="WARNING"/>
            </node>
            <node class="string" name="com.goldencode.p2j.ui.client.widget.TitledWindow">
              <node-attribute name="value" value="WARNING"/>
            </node>

If we want these events to be logged, we simply change the level to INFO for the same entries.

#7 Updated by Greg Shah 11 months ago

Let's use FINER or FINEST instead of INFO.

#8 Updated by Hynek Cihlar 11 months ago

Paula, I like the idea. Two suggestions.

1. I think it would be even better to extend this to all widgets. I.e. pick the interesting events involved in UI navigation (CHOOSE, MOUSE events, etc.) and log those at a location where they are dispatched to the widgets.

2. Document this in Debugging_GUI.

#9 Updated by Paula Păstrăguș 11 months ago

  • % Done changed from 0 to 50

Committed finest level logging for window open/close and mouse click events as rev 16135 / 10451a.

Next, I’ll review which additional events might be useful to log. Not all events will be included, since we want to avoid unnecessary overhead even in a testing system. I’ll focus on those that provide the most value.

#10 Updated by Paula Păstrăguș 11 months ago

  • Status changed from WIP to Review
  • File example.logMagnifier added
  • % Done changed from 50 to 100
Committed finest-level logging for TAB and ENTER key events in rev 16136. At this point, we have logging in place for:
  • window open / close
  • widget clicks
  • TAB / ENTER key input events

This should be sufficient for now, since navigation typically occurs through TAB, ENTER, or mouse clicks. I’ve attached an example log output for reference.

Hynek, please review.

#11 Updated by Hynek Cihlar 11 months ago

  • Status changed from Review to WIP
  • % Done changed from 100 to 90

Code review 10451a.

The "window closed" log output is a bit misleading. It is logged in the remove method of WindowManage. Close operation simetime means a window is closed/hidden rather than destroyed. Also remove is not very meaningful for the navigation, the method is called when a window widget gets out of scope. This could have different timing than just closing a window.

For the key events logging I think it would make it more useful if more information is included in the output, like widget id/name/type/etc. And rather than posting the event it is better to log the event when it is dispatched as dispatch closer to the related UI actions.

Please make the added toString output more in line with the existing formats of the other widgets.

Some UI navigation actions are triggered on mouse release, for these cases, click doesn't even need to come.

When creating logger with Class resolution (name) it is better to pass the Class and let the logger facility to come up with the name. This ensures consistency.

#12 Updated by Paula Păstrăguș 11 months ago

Hynek Cihlar wrote:

The "window closed" log output is a bit misleading. It is logged in the remove method of WindowManage. Close operation simetime means a window is closed/hidden rather than destroyed. Also remove is not very meaningful for the navigation, the method is called when a window widget gets out of scope. This could have different timing than just closing a window.

Would it make more sense to handle this on show/hide and log based on the window’s visibility?

Some UI navigation actions are triggered on mouse release, for these cases, click doesn't even need to come.

I’m not sure I fully understand what you’re trying to highlight here. If we also add logs on mouse release, it would just duplicate the existing mouse clicked logs in this branch, since a mouse click is already the combination of press + release.

#13 Updated by Paula Păstrăguș 11 months ago

Hynek Cihlar wrote:

For the key events logging I think it would make it more useful if more information is included in the output, like widget id/name/type/etc. And rather than posting the event it is better to log the event when it is dispatched as dispatch closer to the related UI actions.

When you mention dispatch, I assume you’re referring to the code in TC.apply around line 4953:

         try
         {
            doInteractive(() -> applyWorker(event));
         }

This is where the event gets applied to the corresponding in focus widget. Do you want me to add the logging there?

#14 Updated by Hynek Cihlar 11 months ago

Paula Păstrăguș wrote:

Would it make more sense to handle this on show/hide and log based on the window’s visibility?

I think so.

I’m not sure I fully understand what you’re trying to highlight here. If we also add logs on mouse release, it would just duplicate the existing mouse clicked logs in this branch, since a mouse click is already the combination of press + release.

Click is a combination of press + relase, but both must happen in a short time frame. If they are further appart no click event is generated. Try button widget for example, press a mouse on it, wait a couple of seconds and then release.

#15 Updated by Hynek Cihlar 11 months ago

Paula Păstrăguș wrote:

Hynek Cihlar wrote:

For the key events logging I think it would make it more useful if more information is included in the output, like widget id/name/type/etc. And rather than posting the event it is better to log the event when it is dispatched as dispatch closer to the related UI actions.

When you mention dispatch, I assume you’re referring to the code in TC.apply around line 4953:

[...]

This is where the event gets applied to the corresponding in focus widget. Do you want me to add the logging there?

I meant the location where events are dequeued from the event queue and processed. TC.processProgressEvent seems like a good fit.

#16 Updated by Paula Păstrăguș 11 months ago

Hynek, if we add the logging directly in ThinClient, the web_gui log will quickly fill up with entries like:

INFO | com.goldencode.p2j.ui.chui.ThinClient | PID:24187, ThreadName:main, Session:00000018, ThreadId:00000001, User:bogus | Drawing with null widget

I’m not convinced ThinClient is the right place for key input events logs, since many of the messages logged there aren’t really intended for debugging. The log file size increases very quickly.

As an alternative, we could make an exception and log these at WARNING (or another higher level), so we don’t end up recording every detail just for debugging purposes.

#17 Updated by Hynek Cihlar 11 months ago

Paula Păstrăguș wrote:

Hynek, if we add the logging directly in ThinClient, the web_gui log will quickly fill up with entries like:

Please post the diff.

#18 Updated by Paula Păstrăguș 11 months ago

I attached the patch. (reverted window manager, event manager, logs for show / hide are now handled by TitledWindow and ModalWindow. In TC I've added logs for tab / return / choose key).

What I wanted to highlight in my prev note can be seen in the attached logs.

#19 Updated by Hynek Cihlar 10 months ago

Paula Păstrăguș wrote:

I attached the patch. (reverted window manager, event manager, logs for show / hide are now handled by TitledWindow and ModalWindow. In TC I've added logs for tab / return / choose key).

What I wanted to highlight in my prev note can be seen in the attached logs.

Please puth a break point where "Drawing with null widget" is logged and see what cases cause these. I suppose there will be a small number (if not one) place where null widget is passed to eventDrawingBracket.

#20 Updated by Paula Păstrăguș 10 months ago

It is caused because we're posting COM-EVENTS with a null source:

ServerEvent{ (-1), source=null, id=1850, name=COM-EVENT, resourceId=1619238176, eventDrawing=false}

Those will be treated as follows: TC.waitForEvent -> applyWorker(Event) -> eventDrawingBraket(...).
The server events are emitted from the ps timer ticks.

#21 Updated by Hynek Cihlar 10 months ago

Paula Păstrăguș wrote:

It is caused because we're posting COM-EVENTS with a null source:

[...]

Those will be treated as follows: TC.waitForEvent -> applyWorker(Event) -> eventDrawingBraket(...).
The server events are emitted from the ps timer ticks.

For these events (ServerEvent with null source) we should not use eventDrawingBracket.

#22 Updated by Paula Păstrăguș 10 months ago

  • % Done changed from 90 to 100
  • Status changed from WIP to Review

Committed changes as rev 16137.

Hynek, please review.

#23 Updated by Hynek Cihlar 10 months ago

Code review 10451a.

Instead of outputting custom message for evety distinct key code, just make the output generic. A single message, to get the name of the event based on code, call Keyboard.eventName.

Logging the event at the location in processProgressEvent is too early. It is still far to reach the target, there are multiple paths leading to early exit not reaching the target widget. Instead I suggest to log the events in TitledWindow.processEvent.

ButtonListGuiImpl.Item toString output should hold the class name including the enclosed class: ButtonListGuiImp.Item. Also, should textWidget.getText() be protected for null?

If the log output should function as a tool for tracing UI flow then it is better to have a dedicated logger for all of these outputs. Enabling the logger will only bring in the needed set of outputs not spoiling the log with other entries.

#24 Updated by Paula Păstrăguș 10 months ago

  • % Done changed from 100 to 80
  • Status changed from Review to WIP

Hynek Cihlar wrote:

If the log output should function as a tool for tracing UI flow then it is better to have a dedicated logger for all of these outputs. Enabling the logger will only bring in the needed set of outputs not spoiling the log with other entries.

Hynek, are you suggesting that instead of using the CentralLogger, we should use a custom logger that can be enabled through directory.xml..?

#25 Updated by Greg Shah 10 months ago

I like the idea of a separate output log but I don't want to have more than one logging infrastructure. Let's try to use CentralLogger (enhanced as needed) for this.

#26 Updated by Hynek Cihlar 10 months ago

Paula, what I meant was using CentralLogger but through a dedicated logger instance. Normally, we have one instance per Java class, retrieved via CentralLogger.get(), but for UI flow logging we would use a single dedicated instance shared across all places that log the UI flow.

This instance could be wrapped in a new class (e.g., UITrace or a similar name) and exposed through a static public method like getLogger.

UI tracing would then be enabled using the existing logging infrastructure and its configuration. No changes to the logging infrastructure itself would be required.

#27 Updated by Paula Păstrăguș 10 months ago

Hynek, thanks for the detailed review!

Quick question: should we log every key input handled in TitledWindow.processEvent, or limit it to these keys: TAB, RETURN, and CHOOSE?

#28 Updated by Hynek Cihlar 10 months ago

Paula Păstrăguș wrote:

Hynek, thanks for the detailed review!

Quick question: should we log every key input handled in TitledWindow.processEvent, or limit it to these keys: TAB, RETURN, and CHOOSE?

I think it will be useful to also log all real keys (KeyInput.isRealKey()) beside the events above.

#29 Updated by Paula Păstrăguș 10 months ago

Hynek Cihlar wrote:

Also, should textWidget.getText() be protected for null?

textWidget is never null since it’s initialized in the constructor. getText() can return null, but that doesn’t cause issues. It’s actually useful to log null values in the web client logs to confirm when no text has been set.

#30 Updated by Paula Păstrăguș 10 months ago

  • Status changed from WIP to Review
  • % Done changed from 80 to 100

Addressed review as rev 16138.

Hynek, please review.

#31 Updated by Hynek Cihlar 3 months ago

  • % Done changed from 100 to 90
  • Status changed from Review to WIP

Code review 10451a revisions 16134..16138

  • [MAJOR] functional ButtonListGuiImpl.Item.toString: textWidget can be null when toString() is called. Add a null guard.
  • [MAJOR] functional MouseHandler.applyMouseEvent: Mouse release events (MOUSE_RELEASED) are not logged. The issue spec explicitly requires mouse release logging, but only MOUSE_CLICKED has the UI_LOGGER.finest(...) call added.
  • [MAJOR] performance MouseHandler.applyMouseEvent: UI_LOGGER.finest(mouseSource + " was clicked.") performs unconditional mouseSource.toString() and string concatenation on every mouse-click event, regardless of whether FINEST is enabled. Use UI_LOGGER.log(Level.FINEST, () -> ...) or guard with isLoggable.
  • [MINOR] performance TitledWindow.dispatchEvent: The key-dispatch logging block unconditionally calls Keyboard.eventName(action) and builds the concatenated log string even when FINEST is disabled. The isLoggableKey guard filters by event kind but not by log level. Wrap the entire block in a UI_LOGGER.isLoggable(Level.FINEST) guard.
  • [MINOR] performance TitledWindow.show: UI_LOGGER.finest("Window " + this + " was shown.") performs unconditional string concatenation and toString() on every invocation regardless of log level. Same applies to TitledWindow.hide and ModalWindow.show. Use UI_LOGGER.log(Level.FINEST, () -> ...) or guard with isLoggable.
  • [MINOR] style UITracker: The license block uses a leading space before ** on every line ( ** text) instead of the standard ** text format used in the module header.
  • [MINOR] style TitledWindow: Two explicit class imports (CentralLogger and UITracker) from the same package com.goldencode.p2j.util.logging instead of a single wildcard import.

#32 Updated by Paula Păstrăguș 3 months ago

  • % Done changed from 90 to 100
  • Status changed from WIP to Review

Addressed review as rev 16139.

Next, I'll rebase the branch to retest the behavior.

#33 Updated by Paula Păstrăguș 3 months ago

I rebased 10451a, new rev is 16521. (is should have been 16520, but the last rev contains a small rebase fix).

#34 Updated by Paula Păstrăguș 3 months ago

I performed some initial testing and it appears to work as expected. I'll continue with more extensive testing once it reaches the internal testing phase ( + also document this within the Debugging GUI wiki afterwards).

However, to ensure that the logs are visible in the web client log file, we need to include the following node in the loggers container within directory.xml:

            <node class="string" name="com.goldencode.p2j.util.logging.UITracker">
              <node-attribute name="value" value="FINEST"/>
            </node>

#35 Updated by Hynek Cihlar 3 months ago

Logging the keyboard events (and the types keys) is a potential security issue (consider passwords entered by the end users). Greg, what do you think? Should we deliver code that could be misused for spying on users?

#36 Updated by Paula Păstrăguș 3 months ago

From my POV, this feature is meant strictly for a testing environment, not for production use. Still, you raise a fair point, it could introduce a security concern. However, logging the typed keys is genuinely helpful when a problematic or unexpected character causes the application to behave incorrectly.

#37 Updated by Hynek Cihlar 3 months ago

Paula Păstrăguș wrote:

From my POV, this feature is meant strictly for a testing environment, not for production use. Still, you raise a fair point, it could introduce a security concern. However, logging the typed keys is genuinely helpful when a problematic or unexpected character causes the application to behave incorrectly.

Yes, I agree the feature is useful.

#38 Updated by Paula Păstrăguș 3 months ago

Maybe we can exclude password fill-ins from logging. There is a place in counteract where users actually enter passwords, so filtering those out would avoid exposing sensitive data.

#39 Updated by Greg Shah 3 months ago

I agree this is a potential security risk. It is enabled purely by setting a logging level, right? That is something that could be easily overlooked.

Let's add a one time WARNING entry to the logs when this logging is active. This should be something like "Keylogging is activated on this server. This may cause a SECURITY or PRIVACY breach. It should only ever be enabled in a TEST or DEVELOPMENT environment, this is not suitable for production!".

With this, it should be more likely to catch a mis-configuration.

#40 Updated by Hynek Cihlar 3 months ago

  • Status changed from Review to WIP
  • % Done changed from 100 to 90

Code review 10451a revisions 16515..16521

  • [MAJOR] performance TitledWindow.processEvent: In the new KeyInput branch, keyEvent.actionCode(), Keyboard.eventName(action) (string allocation/lookup), and keyEvent.isRealKey() are all invoked on every dispatched key event BEFORE UI_LOGGER.isLoggable(Level.FINEST) is checked. Key dispatch is a hot path; this imposes per-keystroke overhead even when FINEST is disabled. The history entry advertises "Guarded finest logging in ... key dispatch processing", but the guard is placed after the expensive computation. Move the isLoggable check outer-most so the eventName computation is only performed when logging is enabled.
  • [MAJOR] functional TitledWindow.show/hide: The diff adds import com.goldencode.p2j.ui.client.gui.OverlayWindow; and two instanceof OverlayWindow guards into TitledWindow (package com.goldencode.p2j.ui.client.widget), which had no GUI-package dependencies and is the base of CHUI widgets such as OuterFrame. Leaking a concrete GUI subtype reference into a driver-agnostic base inverts the dependency direction. Since TopLevelWindow already declares an abstract getRelativeTo(), either (a) introduce a protected boolean isTransientCursorRelative() hook on TitledWindow (default false, overridden by OverlayWindow to return true when getRelativeTo() == RELATIVE_TO_CURSOR), or (b) move the logging into TopLevelWindow.show/hide where getRelativeTo() is already in scope without a cast.
  • [MINOR] functional ModalWindow.show: The "Window ... was shown." log is emitted before any of the actual show work - realize(), forceVisibility(true), gd.setWindowVisible(true), WindowManager.modalizeWindow(this), WindowManager.forceActivateWindow(this, true). Any of these can throw, leaving the trace claiming success when the window was never actually shown. Move the log to after those steps, or change wording to "is being shown.".
  • [MINOR] functional Item.toString: Hardcodes the class name as the literal "ButtonListGuiImpl.Item" instead of using getClass().getSimpleName() or delegating to super.toString(). AbstractWidget.toString() already emits getClass().getSimpleName() + " [id=" + Utils.nonNull(getId(), "<null>") + "]", and MenuItemGuiImpl.toString() correctly follows the super.toString() + " title=" + ... pattern. Item is non-final so hardcoding defeats the existing pattern and breaks on subclass/rename.
  • [MINOR] functional Item.toString: textWidget.getText() may itself return null (the underlying TextCell.getText() returns the text field unguarded, and setText() permits null), yielding title=null in the output. Use Utils.nonNull(textWidget.getText(), "") for consistency with TitledWindow.toString().
  • [MINOR] performance TitledWindow.hide: The instanceof OverlayWindow test, cast, and getRelativeTo() call are evaluated on every hide() before the isLoggable guard. Reorder so UI_LOGGER.isLoggable(Level.FINEST) is checked first to short-circuit when FINEST is off.
  • [MINOR] performance TitledWindow.show: Same pattern as hide(); put isLoggable first in the outer check so the instanceof/getRelativeTo() work is skipped when logging is disabled.
  • [MINOR] style TitledWindow: New import java.util.logging.Level; uses an explicit class import where the project convention is .* unless a conflict requires an explicit import.
  • [MINOR] style MouseHandler: New import java.util.logging.Level; uses an explicit class import where the project convention is .*.
  • [MINOR] style TitledWindow: New import com.goldencode.p2j.ui.client.gui.OverlayWindow; is an explicit single-class import; the convention is com.goldencode.p2j.ui.client.gui.* unless a naming conflict exists. (Note: introducing this import at all is flagged separately as an architectural concern.)

#41 Updated by Paula Păstrăguș 3 months ago

  • Status changed from WIP to Review
  • % Done changed from 90 to 100

Addressed code review as rev 16522. I also added the one time warning.

Hynek, please review. Thanks!

#42 Updated by Hynek Cihlar 3 months ago

  • Status changed from Review to WIP
  • % Done changed from 100 to 90

Code review 10451a revisions 16515..16522

  • [MAJOR] security UITracker.<clinit>: The keylogger warning is emitted only at Level.FINEST and only inside the class's static initializer. (1) It is logged at the same level as the keystrokes themselves, so an operator who has turned on FINEST for UITracker but routes only INFO+ to operator-visible sinks will never see the warning. The Redmine summary explicitly states the warning should be at WARNING level. (2) The warning fires at most once - at class-load time; if FINEST is enabled later via CentralLogger.setLevels() the warning never fires while keystroke logging starts working. (3) Conversely, if the class loads before FINEST is enabled, the static init guard if (LOG.isLoggable(Level.FINEST)) evaluates false and the warning is never emitted.
  • [MAJOR] functional Item.toString: textWidget itself can be null. The field is initialized lazily in createOrUpdateComponents()/recomputeSize(), which has an early-return path when window() null, leaving textWidget uninitialized. The expression textWidget.getText() will throw NPE before Utils.nonNull ever sees the value. The history comment says "Added NPE protection," but Utils.nonNull(...) only protects against a null return from getText(), not against textWidget itself being null. With the new MouseHandler logging using mouseSource + " was clicked.", an Item whose layout has not yet completed (or after a window destroy that nulls children) will throw NPE inside applyMouseEvent, propagating through handleMouseEvent and corrupting the UI event chain. Add a null guard, e.g. textWidget null ? "" : Utils.nonNull(textWidget.getText(), "").
  • [MINOR] performance TitledWindow.processEvent: Keyboard.eventName(action) is computed before the isLoggableKey check. For non-loggable keys (the vast majority of keystrokes - any modifier, function key, etc., that is not TAB/RETURN/CHOOSE/real-key), this produces a wasted String allocation per key event whenever FINEST is enabled. Move the eventName computation inside the if (isLoggableKey) block so it only runs when actually needed.
  • [MINOR] functional TitledWindow.show: The FINEST log fires near the end of TitledWindow.show() but BEFORE subclass code in OverlayWindow.show() and WindowGuiImpl.show() completes (e.g., gd.setWindowVisible(true)). The log records that the window "was shown" before the driver-level visibility is actually set. By contrast, ModalWindow.show() logs at the end after forceActivateWindow() (correct timing). For consistency, consider moving the log to occur after the full subclass-specific show sequence.
  • [MINOR] functional ModalWindow.show: Logging is duplicated logic instead of leveraging super.show(). The rest of the subclass hierarchy (OverlayWindow, WindowGuiImpl, FrameOverlay, DialogBoxWindow, AlertBoxGuiImpl) calls super.show() to inherit logging, but ModalWindow.show() does not call super.show() (it has its own divergent implementation). The added log message string is hand-copied from TitledWindow.show(). If the format diverges, the two paths drift. Suggested fix: extract a small helper such as logShown() on TitledWindow that both paths invoke.
  • [MINOR] functional ModalWindow.show: The show log lacks the !isTransientCursorRelative() guard used in TitledWindow.show()/hide(). Since ModalWindow is never cursor-relative this is currently harmless, but if a future modal subtype needs to suppress its show log there is no hook. Suggested fix: gate the log with the same guard for design consistency.
  • [MINOR] functional Item.toString: Formatting deviates from the established convention. Other GuiImpl toString() methods (e.g. AbstractWidget, BrowseGuiImpl, TitledWindow) use getClass().getSimpleName() + " [id=..." with a leading space before the bracket; the new code emits getClass().getSimpleName() + "[id=..." with no space, producing output like Item[id=...] instead of Item [id=...]. Add the leading space for visual consistency in logs.
  • [MINOR] functional Item.toString: The override returns getClass().getSimpleName() + "[id=...]" and entirely drops the parent's super.toString() info. The convention in sibling classes (MenuItemGuiImpl, SubMenuGuiImpl) is to extend it via super.toString() + " title=" + .... Not a defect but consider chaining to super.toString() to keep any future parent-added fields visible and avoid duplicating the id/class header.
  • [MINOR] functional MouseHandler.applyMouseEvent: The new FINEST log is placed AFTER the call to mouseSource.mouseClicked(evt) / mouseSource.mouseReleased(evt). If the widget's handler throws (or terminates the dispatch via an exception path), the FINEST entry will not be emitted, so the log will not reflect that the event was actually delivered. Additionally, if a click handler causes the widget to be destroyed before the log statement executes, toString() could touch null/disposed state (especially relevant given the new ButtonListGuiImpl.Item.toString() override that dereferences textWidget). Suggested fix: move the log line above the dispatch, or wrap it in try/finally.
  • [MINOR] functional MouseHandler.applyMouseEvent: The chosen pair MOUSE_CLICKED + MOUSE_RELEASED (without MOUSE_PRESSED) is asymmetric and likely accidental. The natural mouse interaction sequence is PRESSED -> RELEASED -> CLICKED; logging RELEASED and CLICKED but not PRESSED makes the trace harder to interpret (e.g. drag-then-release-outside scenarios produce a RELEASED log with no preceding PRESSED context). Suggested fix: either also add a FINEST log in the MOUSE_PRESSED branch, or document why MOUSE_PRESSED is intentionally suppressed.
  • [MINOR] functional MouseHandler.applyMouseEvent: The log message mouseSource + " was clicked." discards the actual MouseEvent payload (button, click count, modifiers, coordinates). MOUSE_CLICKED can fire with click counts > 1 (double-click) and with different buttons (BUTTON1/2/3); the log entry as written cannot distinguish a left-click from a right-click or a single from a double-click on the same widget. Suggested fix: include evt.getButton(), evt.getClickCount(), and possibly evt.getX()/evt.getY() in the log message.
  • [MINOR] functional TitledWindow.processEvent: When the inner source.processEvent(event) triggers a focus change or destroys the widget, the subsequent UI_LOGGER.finest("'" + eventName + "' key dispatched for source = " + source) calls toString() on a potentially-disposed widget, which can in turn call user code (configured toString overrides like the new ButtonListGuiImpl.Item.toString which can NPE). Move the log line above source.processEvent(event) (with wording adjusted to "dispatching") to keep diagnostics safe and to also capture cases where the dispatch throws. Even if the widget destroy is currently impossible I think it makes sense to harden the code for potential future changes.
  • [MINOR] style ModalWindow: Copyright year in the file header is 2015-2025 but new history entries are dated 20260415. Update the copyright to 2015-2026.
  • [MINOR] style TitledWindow: Copyright year is updated only to 2010-2025, but new history entries are dated up to 20260423. Update copyright to 2010-2026.

#43 Updated by Paula Păstrăguș 3 months ago

  • Status changed from WIP to Review
  • % Done changed from 90 to 100

Addressed code review as rev 16523.

#44 Updated by Hynek Cihlar about 2 months ago

  • Status changed from Review to Internal Test

Code review 10451a revisions 16515..16523

The changes look good, please address the following style issue and go ahead with regression testing. Also please update the wiki unless you already did.

  • [MAJOR] style MouseHandler.logMouseEvent: Line 942 is 134 characters, exceeding the 110-character limit. The UI_LOGGER.finest(mouseSource + " was clicked. Button = " + evt.getButton() + ", click count = " + evt.getClickCount()) call must be wrapped across multiple lines.

#45 Updated by Paula Păstrăguș about 2 months ago

Addressed as rev 16524.

I'm starting regression testing.

#46 Updated by Paula Păstrăguș about 2 months ago

The wiki has been updated and is available at Debugging GUI.

Regression testing did not reveal any issues. I smoke tested Counteract and Hotel GUI, both with logging enabled and disabled, and everything looked fine.

Please let me know if there is another large GUI application that should be tested as well, and I will ask Serban to do it. Thanks!

#47 Updated by Hynek Cihlar about 2 months ago

Paula Păstrăguș wrote:

Please let me know if there is another large GUI application that should be tested as well, and I will ask Serban to do it. Thanks!

Please also run ChUI regression tests.

#48 Updated by Paula Păstrăguș about 2 months ago

10451a was rebased, new rev is 16589.

#49 Updated by Paula Păstrăguș about 2 months ago

I started the ChUI regression tests. In the best case scenario, we should have the results by EOD.

#50 Updated by Paula Păstrăguș about 2 months ago

Here are the results from yesterday:

  • gso_282 failed: expected.
  • gso_320 failed: not expected. However, Teodor also ran the ChUI regression tests and this failed there as well, so it may be a false positive.
  • gso_418 failed: expected.
  • gso_478 failed: expected.
  • tc_inquiry_inventory_009 failed: not expected. However, after seeing the failures, I ran the ChUI regression tests again and this test passed..
  • tc_job_matlcron_002 failed: expected.
  • tc_ap_vchrs_011 failed: not expected. Same as above, this passed on the second run.

It looks like something in trunk is causing some kind of non-deterministic behavior. Teodor mentioned that he will run the tests using a fresh setup with trunk, so let's wait for those results and see what we can conclude.

From my point of view, these failures should not be caused by 10451a. Most of the changes are guarded by the UITracker logger configuration in directory.xml, if that logger is not enabled, the behavior should remain unchanged. The only change I can think of outside that area is the one from TC, but that is only NPE protection.

#51 Updated by Paula Păstrăguș about 2 months ago

I have Serban's approval for testing with another application and Teodor's approval for the ChUI Regression Testing. If there is nothing else left to test, I think we can proceed with the merge.

#52 Updated by Hynek Cihlar about 2 months ago

  • Status changed from Internal Test to Merge Pending

Please merge 10451a to trunk.

#53 Updated by Paula Păstrăguș about 2 months ago

  • Status changed from Merge Pending to Test

Branch 10451a was merged into trunk as rev. 16583 and archived.

Also available in: Atom PDF