Release v0.1.0#96
Merged
Merged
Conversation
Task-Url: #60 * **Manual Connection Toggle:** Added a mode-driven "Connect" toggle to the top bar that adapts based on the environment (hidden in Portable, disabled in Local, enabled in Interactive). * **Isolated Testing Configuration:** Configured the handler to build a temporary, isolated test config and a hidden `__ledspicerui_test__` profile, keeping testing completely independent of standard profiles. * **Daemon Takeover & Clean Restore:** Implemented a single-step discovery process that captures running daemon states. It gracefully stops the daemon via `systemctl` or `SIGTERM`, and safely restores it afterward using a detached launch (`setsid`) so it remains completely independent of the UI lifecycle. * **Targeted Hardware Testing:** Implemented generic command sending where UI elements map directly to hardware actions. Strips now pack evenly into display cells, and interacting with a segment cleanly lights or clears the entire block. * **Context-Aware UI Controls:** Restricted element testing so it is only available while actively connected, and automatically deactivates any active element if a disconnect occurs. * **Visual Transition Overlay:** Integrated a modal, borderless busy dialog with an auto-starting spinner and label that pumps the main loop during the brief connection/disconnection window. * **Flashed Debug Feedback:** Added a non persistent "Verbose UI Debugging" option in Settings that routes semantic test actions through a new, fast-fading `Debug` severity level. * **Terminal & UI Cleanups:** Removed dead observers, silenced redundant console noise, and refined the spacing and margins of the Settings layout.
60: Real daemon IPC for layout test commands
Design decision: - Moved the testing check from individual components into a single central gate: command() checks readiness before sending. - Component UI-disabling remains separate and is purely visual; message validation happens at the central gate. DaemonHandler: - Added a public std::function<bool()> isReady predicate, set at initialization. - command() returns false immediately if isReady() evaluates to false. - Made isRunning() private; disconnect() now skips the killall command when no daemon is running (removing the "no process found" stderr message). MainWindow: - Added ensureDaemonReady(): returns false if offline; if the configuration is stale, it reconnects and cancels the current test; returns true when online and fresh. This is injected into DaemonHandler at initialization. - Built the test sandbox after settings load (once the mode is known) rather than in the constructor—this fixes the bug where connecting did nothing until Settings had been opened at least once. - The refresh process pauses consumers before relaunching and resumes them on success; it drops the connection on failure. Consumers are disabled before the loop is pumped, eliminating the need for a re-entrancy guard. Layout / LayoutElement: - Removed all reconnection and stale logic (ensureTestable, reconnect), which is now handled by MainWindow. - Gating simplified to a basic testing flag for visual management; removed the unused Settings dependency. - Added LayoutElement::stopTesting() to cancel active tile lighting and timers when paused. - lightTarget and clearTarget now return the send result, ensuring a strip group stops if testing is paused mid-group. - Inlined the single-use deactivate() method directly into setTesting(). DaemonSandbox: - Made baseDir a const variable initialized in the constructor initializer list. - Aligned getter documentation with the project style guidelines. Settings: - Removed the obsolete setMode() method. Comments: - Trimmed the readiness-gate and testing comments to remove overly long explanations.
Task-Url: #84 Adds a Test affordance to the Restrictor dialog that drives the `rotator` binary so users can validate restrictor wiring without the command line. - The way icons become a single-active toggle group (none allowed, re-click clears, never assumes hardware position). Toggling a way runs `rotator -c <sandbox.conf> <player> <joystick> <ways> …` for the restrictor's player mappings. - Three modes drive the affordance: **portable** (icons informational), **local** (disabled hint), **interactive + connected** (live). - Staging reuses the daemon sandbox + `ensureDaemonReady()`; restrictor edits now stale the staged config so the test re-stages fresh data. - Multi-interface restrictors with 2+ mappings can scope the test to selected rows (none selected == all). - Output goes to the status bar: stdout only when "debug hardware test" is on, red on a non-zero exit or stderr. - `Settings`: `debugDaemon` → `debugHardwareTest`, `daemonStale` → `configDirty`. - `Defaults::runCommand`: output-less overload + stderr folded into output. - Way tokens aligned to rotator's CLI vocabulary; `Ways` stays in `Defaults`, the way-token map + arg builder live in `DialogRestrictor`. - `DaemonHandler` readiness gate installed via a setter. - `RestrictorTest`/`DefaultsTest` updated; full suite green (27/27). - Verified on hardware.
84: Restrictor dialog: add Test button driving the rotator binary
Task-Url: #85 The test toggle was gated on LED elements, meaning a restrictor-only project (no LED hardware) could never enter test mode, leaving the rotator test unreachable. Reframe the single toggle as a shared sandbox configuration with two zones: the daemon zone (devices, layout test) and the restrictor zone (rotator test). Test mode is available when either zone contains data; staging is always performed, while ledspicerd launches only when devices are present. - updateDaemonControls: Eligibility is now based on (devices and port) OR restrictors. The port requirement applies exclusively to the daemon zone. - launchDaemon: Always stage the sandbox configuration; connect the daemon only when devices exist. - connectDaemon/reconnectDaemon: Drive layout consumers and the "Daemon connected" status exclusively for the daemon zone. - Relabel the toggle tooltip and status messages to remove daemon-specific wording.
85: Test mode for restrictor-only configs
than it was created in Task-Url: #88 A project saved under one mode (Portable vs Local) resolved its ledspicer.conf to the other mode's location on reload, so ConfigFile threw and openProject silently wiped the UI — project name in the title, nothing populated. - Derive a project's origin from disk (project-local ledspicer.conf => Portable, absent => Local) and refuse to open or save it under a mismatching mode, with a clear message and no state change. - On an unreadable/corrupt config, report and stay idle instead of entering an empty, editable project. - Validate new project names before creating: require a projects directory, a unique name, and a non-reserved ".bak" suffix; flag the entry inline. Add read-only Defaults::comboBoxHasId for the lookup. - Stop the color-combo refresh from marking the project dirty with no project open. - Add Settings::getModeLabels for the mismatch messages.
88: Fix empty UI when a project is opened under a different mode
* 89: Save validation rejects valid restrictor-only projects Task-Url: #89 Refactor DaemonSandbox to use sandboxDir and improve profile handling
Task-Url: #91 Bug fixes: - Random colors list no longer carries over stale entries between project loads (boxRandomColors is now wiped unconditionally before repopulating). - Settings > General tab: Switch/Label order swapped to match the rest of the app (Label first, then Switch), and labels given expand=True so all switches align flush-right within their row. - Removed 3 dead `.clear()` calls in readConfigFile (redundant with load()'s own internal clear). - Removed a stale, contentless TODO comment in DialogLinkEditor.cpp. - Re-selecting the already-open project now offers a real revert (discard unsaved changes + reload from disk) instead of just an info message, when the project is dirty. Save-time "All" group optimization: - ConfigFile::save() now omits the "All" group from the saved file when it exactly matches the default device order (position-for-position), since the daemon auto-builds it identically in that case. - Strip elements are expanded into their children (not the unlinkable parent) when building that comparison, matching daemon behavior. - xmlSection() gained an `allowEmpty` parameter so `<layout>`'s defaultProfile/defaultProject attributes still get written even when the groups body ends up empty after "All" is omitted. Code quality / review follow-ups: - Moved ProjectFile.hpp / Storage/Element.hpp includes into ConfigFile.hpp (header carries the deps, not the .cpp). - Multi-line comments in ConfigFile.cpp switched from stacked `//` to `/* */`. - Extracted the strip-expansion and order-comparison logic out of save() into two named, testable static methods: expandDeviceElements() and matchesDeviceOrder(). Tests: - Added 7 unit tests covering both extracted helpers: plain element order, strip-parent exclusion (children only, correct position), mixed plain+strip sequencing, and matching cases for matchesDeviceOrder (identical, reordered, size mismatch, strip children reordered). - Added tests/mocks/MockDevice.hpp (reusable minimal Parent stand-in for a device), matching the existing MockData/MockBasicData conventions. - Verified with a deliberate negative control (temporarily broke matchesDeviceOrder) to confirm the new tests actually catch the regression they're meant to catch, not passing vacuously. Investigated, found not to be bugs: - "Default profile not selected after loading" — confirmed to be expected behavior in Local Mode, where projects share one physical ledspicer.conf file (not a defect). - Cascade delete on Element removal (dependency system correctly purges stale Group links, including in "All", when devices are wiped) — verified correct, no dangling pointers.
Task-Url: #90 - Settings: replace global Portable/Local/Interactive mode with per-project ConfigSource (embedded config → Project, else System) resolved from disk at project selection; add hasBinary(), isRootConfigWritable(), isProjectDirWritable(), isSystemConfigAvailable(), isPathWritable(). Remove mode-mismatch guards: opening always reads the correct config location. - Read-only lock-down: widgets carrying RoLockedConfig/RoLockedProject Glade markers desensitize (or flip edit→view) via Defaults::applyWritability, swept over toplevels and re-applied per item on display. Save skips a read-only source after confirmation; partial saves reported on the status bar. - Path choosers freeze while the project is dirty; a binary change reloads the open project. - Project dialog: portable toggle for new projects, config-location conversions (make portable / deploy to system / use system config), subtitle shows the source and a lock marker when read-only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First tagged release of LEDSpicer UI — the visual editor for the LEDSpicer
platform.
Highlights
Notes
Built with GTKmm 3 + tinyxml2.