Skip to content

feat: migrate primary UI to QML - #3

Merged
NullifiedSec merged 109 commits into
NullifiedSec:mainfrom
magetsu002:feat/qml-ui-migration
Sep 10, 2026
Merged

NullifiedSec merged 109 commits into
NullifiedSec:mainfrom
magetsu002:feat/qml-ui-migration

Conversation

@magetsu002

@magetsu002 magetsu002 commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR migrates Hyprbind's primary configuration UI to QML while keeping the existing Rust parsing, validation, backup, bundle, and writer paths authoritative.

QML is now the normal launch path. The existing GTK UI remains available through hyprbinds --gtk as a compatibility path for the developer-only companion studios while those surfaces are still being migrated.

What changed

  • adds the QML application shell, shared theme/material components, navigation, page header, status bar, dialogs, and reusable controls
  • migrates Overview, Binds, Variables, Environment, Submaps, Startup, Window Rules, Workspace Rules, Layer Rules, Look & Feel, Config, Monitors, Devices, Animations, Curves, Gestures, Health, Logs, and Import / Export
  • adds CXX-Qt bridges for QML-facing state and mutations while retaining the existing Rust backend as the source of truth
  • preserves existing atomic writer/backup and bundle import/export behavior rather than introducing QML-side config serialization
  • restores Binds submap filtering, conflicts-only filtering, and dispatcher-category browsing in the QML primary UI
  • restores Health copy-report, copy-system-info, and per-check copy-fix actions
  • keeps the developer-only Wallpaper, Waybar, Starship, VIA, Screenshare, and Audio studios available through the GTK compatibility path
  • adds responsive wide/medium/compact layouts and consistent page icon identity
  • keeps editor focus surfaces neutral and sidebar selection atomic
  • scopes the former Realtime control to Live preview, matching its QML behavior instead of implying the old global autosave semantics
  • documents the Qt/QML + CXX-Qt native build requirements
  • adds a pull_request trigger to the QML migration workflow

Architecture

QML owns presentation and interaction. Rust continues to own configuration discovery, parsing, validation, conflict handling, backups, persistence, import/export, and Hyprland-facing operations.

The migration intentionally reuses the existing writer/model paths instead of reimplementing config semantics in the frontend.

Verification

Verified on Arch/CachyOS + Hyprland at exact head b321bf43c43715efde9d1b44e08f4c62c4f6b6b8:

cargo test --locked
45 passed; 0 failed

cargo build --locked
PASS

hyprbinds
QML launch smoke: PASS

hyprbinds --gtk
GTK compatibility launch smoke: PASS

The target-repo PR workflow is now created from the pull_request trigger. GitHub currently reports the fork run as action_required, so the target maintainer still needs to approve the run before its jobs execute.

Review notes

Please review both the QML presentation layer and the Rust bridge boundaries, especially anywhere configuration mutation crosses from QML into existing writer code. The intended invariant is that QML never becomes an independent config authority.

Copy link
Copy Markdown
Owner

Thanks for the work here — the Rust/QML boundary is generally going in the right direction, especially keeping config mutations routed through the existing Rust writer paths instead of reimplementing config serialization in QML.

I do think this needs a few changes before merge though:

  1. The QML UI is not actually the default UI yet. main.rs only calls qml_bridge::run() when --qml is passed; normal execution still launches the GTK app. The shipped desktop entry also still runs plain hyprbinds, so installed users would continue seeing the GTK UI after this PR merges. If this is intended to be the primary UI migration, QML should become the normal launch path (or the PR should be explicitly scoped as an experimental/opt-in implementation).

  2. Qt is now an unconditional build dependency, but the documented build/install requirements are still GTK-only. Cargo.toml + build.rs always pull/build the CXX-Qt/QML module, while DEPENDENCIES.md still says an Arch build only needs Rust/GTK/pkgconf/base-devel. The new CI workflow itself installs the Qt6/QML packages before building, so the packaging/docs need to reflect those requirements (or Qt should be feature-gated while QML remains optional).

  3. Behavioral parity is incomplete. The current GTK navigation exposes Import / Export plus the developer-mode Wallpaper, Waybar, Starship, VIA, Screenshare, and Audio pages. The QML sidebar currently stops at Health/Logs, so those surfaces disappear if QML becomes the primary UI. Import / Export is not experimental, so at minimum that one is a core regression.

  4. Realtime semantics changed. In the current UI, Realtime means autosaving Config/open editors after the debounce interval and is wired through settings/rules/env/submaps/startup editing paths. In QML, the global Realtime toggle is only consumed by Look & Feel live preview. That should either restore the existing autosave behavior across the migrated editors or be renamed/scoped so it does not imply the old behavior.

  5. The new QML CI workflow does not run as a PR check. It only triggers on pushes to feat/qml-ui-migration plus workflow_dispatch; there is no pull_request trigger. Since this PR comes from a fork, the target PR currently has no associated workflow run/status at the head. For a migration of this size, the target repo should validate the proposed merge state.

So overall: architecture direction looks reasonable, but I would hold merge until the default launch path, dependency contract, parity gaps, realtime behavior, and PR CI coverage are addressed.

@magetsu002

Copy link
Copy Markdown
Contributor Author

Thanks, I fixed all five points.

QML is now the default launch path, with --gtk kept as the compatibility fallback.

Qt/QML build dependencies are now documented.

Import/Export is available in QML and still uses the existing Rust bundle logic.

The experimental tools remain available through the GTK fallback for now.

Realtime was renamed to Live preview so it matches the QML implementation.

The workflow now runs on pull requests too. GitHub currently shows the fork run as action_required, so it needs maintainer approval before the jobs start.

Current head is verified:

cargo test --locked
45 passed, 0 failed

cargo build --locked
PASS

QML launch
PASS

GTK fallback
PASS

NullifiedSec commented Sep 10, 2026 •

Copy link
Copy Markdown
Owner

Follow-up after checking the new head: the original five points are materially fixed. I’m not worried about the README cleanup right now since this is still an active design/migration pass, but I do want to block on lost functionality now that QML is the default UI.

Two regressions still stand out:

  1. Binds lost existing controls. The current GTK Binds UI has a submap filter, a conflicts-only filter, and dispatcher/category tabs. The QML page currently only exposes text search + Add/Edit/Delete. It does load submaps and conflict state from the Rust bridge, but there is no UI to filter by submap/conflicts or browse by dispatcher category. Since QML is now the primary path, those are user-facing regressions rather than deferred polish.

  2. Health lost its support/debugging actions. The GTK Health page has Copy report, Copy system info, and per-check Copy fix actions. The QML Health page renders the diagnostics and fix command text, but its bridge only exposes the snapshot and there are no equivalent copy/report actions. Reload can cover rerunning checks, so that part is fine; the copy/report workflow is what’s missing.

I’m fine with the experimental companion studios staying behind the GTK fallback for now. The main thing I don’t want in this migration is existing primary-UI functionality disappearing just because the presentation layer changed.

@magetsu002

Copy link
Copy Markdown
Contributor Author

The parity issues you pointed out are fixed.

If there are UX changes you want, tell me specifically what feels wrong and what behavior you want changed. "It's ass" doesn't give me anything actionable.

I'm not doing another broad redesign pass though.

@NullifiedSec
NullifiedSec self-requested a review September 10, 2026 17:52
@NullifiedSec

Copy link
Copy Markdown
Owner

Ok i guess you're not ass anymore 👍

@NullifiedSec NullifiedSec left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@NullifiedSec
NullifiedSec merged commit 0eabaec into NullifiedSec:main Sep 10, 2026
1 check passed
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