Skip to content

Notification system#160

Merged
mrpilot2 merged 31 commits into
developfrom
develop+notification_system
Jul 22, 2026
Merged

Notification system#160
mrpilot2 merged 31 commits into
developfrom
develop+notification_system

Conversation

@mrpilot2

Copy link
Copy Markdown
Owner

Implements #142

}
}

TEST_CASE("Any notification manager", "[NotificationManager]")
@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.60820% with 193 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.96%. Comparing base (c041009) to head (b2af991).
⚠️ Report is 32 commits behind head on develop.

Files with missing lines Patch % Lines
...de/src/gui/widgets/notificationlogitemdelegate.cpp 55.14% 61 Missing ⚠️
demo/tests/notificationlauncherdialogtest.cpp 90.63% 25 Missing ⚠️
aide/src/gui/widgets/gotittooltip.cpp 92.33% 21 Missing ⚠️
aide/src/gui/notificationballoonhost.cpp 86.36% 12 Missing ⚠️
aide/src/gui/widgets/notificationballoon.cpp 92.46% 11 Missing ⚠️
demo/src/notificationlauncherdialog.cpp 92.95% 10 Missing ⚠️
...ttings/notifications/notificationssettingspage.cpp 89.53% 9 Missing ⚠️
...ings/notifications/notificationgrouptablemodel.cpp 93.57% 7 Missing ⚠️
tests/ut/mockmainwindowview.cpp 0.00% 5 Missing ⚠️
aide/src/gui/mainwindow.cpp 92.59% 4 Missing ⚠️
... and 13 more
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #160      +/-   ##
===========================================
- Coverage    93.08%   92.96%   -0.12%     
===========================================
  Files          158      201      +43     
  Lines         6075     8673    +2598     
  Branches       276      386     +110     
===========================================
+ Hits          5655     8063    +2408     
- Misses         420      610     +190     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown

Unit Test Results install lib

118 files  +118  118 suites  +118   1s ⏱️ -1s
151 tests +151  151 ✅ +151  0 💤 ±0  0 ❌ ±0 
426 runs  +426  426 ✅ +426  0 💤 ±0  0 ❌ ±0 

Results for commit b2af991. ± Comparison against base commit c041009.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown

Unit Test Results ubuntu-24.04

1 536 files  +  344  1 536 suites  +344   26s ⏱️ +6s
  717 tests +  134    717 ✅ +  134  0 💤 ±0  0 ❌ ±0 
7 328 runs  +1 720  7 328 ✅ +1 720  0 💤 ±0  0 ❌ ±0 

Results for commit b2af991. ± Comparison against base commit c041009.

♻️ This comment has been updated with latest results.

@mrpilot2
mrpilot2 force-pushed the develop+notification_system branch 2 times, most recently from 2602317 to a4bbb95 Compare July 19, 2026 10:19
queue.pending.append(this);
return;
}
queue.current = this;
Comment on lines +42 to +43
TEST_CASE("NotificationGroupTableModel seeded with groups",
"[NotificationGroupTableModel]")
@mrpilot2
mrpilot2 force-pushed the develop+notification_system branch 2 times, most recently from 121dbb6 to 63ac123 Compare July 20, 2026 22:31
@mrpilot2
mrpilot2 force-pushed the develop branch 2 times, most recently from 6387295 to c041009 Compare July 20, 2026 22:52
@mrpilot2
mrpilot2 force-pushed the develop+notification_system branch from 27a0127 to 9c09a27 Compare July 20, 2026 23:12
mrpilot2 and others added 17 commits July 21, 2026 01:14
Locks in the notification value types (Notification, NotificationGroup,
NotificationAction, NotificationId, NotificationType/DisplayType) and
NotificationManagerInterface/NotificationManager per the design decisions
in issue #143 (core API), #145 (display-type routing + DND), and #148's
read-state amendment, wired into Application/ApplicationBuilder mirroring
actionRegistry(). Unblocks the remaining per-surface build tickets (#150-#157).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reusable severity-tinted Banner strip (icon, message, optional action
links, close button) plus MainWindow::addBanner/removeBanner, wrapping
the central widget so several active banners stack vertically with
hairline dividers, newest at the bottom. Caller-driven per #145: never
goes through NotificationManager::post() and is never logged.

Per-type icons and colours are placeholders until #157 lands themed
severity assets.

Closes #150.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Styled QDialog base exposing a banner slot between the title bar and
content, reusing the shared Banner widget from #150. New dialogs only,
per the #146 design decision.

Closes #151
Built exactly per #147: aide::widgets::NotificationBalloon is a frameless
corner popup (severity stripe, icon, title, body, up to two inline action
links plus a "More" overflow menu, close button) mirroring Banner's look.
Transient balloons auto-dismiss after 8s with a countdown bar that pauses
on hover; sticky balloons never time out.

aide::gui::NotificationBalloonHost observes NotificationManagerInterface
and shows a balloon for every Balloon/StickyBalloon-resolved notification,
routed through the concrete NotificationManager's notificationPosted
signal wired in ApplicationBuilder (string-based connect, same cross-DLL
reasoning as the existing AppearanceManager wiring). Corner placement is
the new global aide::NotificationBalloonPlacement setting
(notifications.balloonPlacement, default BottomRight); stacking derives
from the corner alone, growing toward screen center.

Verified with dev-unix-static-debug (warnings-as-errors, ASAN+UBSAN,
clang-tidy, cppcheck); full suite green (775 assertions, 157 test cases).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…`this`

QTimer::singleShot() had no context object, so the queued lambda held a
raw `this` with no lifetime guard. If the widget was destroyed before
VISIBILITY_TOGGLE_DELAY elapsed, the timer fired into freed memory —
caught by ASAN as a stack-use-after-return once test #153's added event
pumping gave the timer enough wall-clock time to actually fire.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements the design locked in #147: a frameless GotItTooltip anchored
to a target widget (Position::{Below,Above,Left,Right}, arrow pointing
at it), caller-driven and independent of NotificationManager (#149) —
only SettingsInterface and a target QWidget are needed.

- Fluent withHeader/withStep/withLink/withShowCount/withTimeout setters,
  terminated by showGotIt(target, position).
- Seen state is an int show-count at SettingsInterface key
  "aide/gotit/<id>" (canShow() == count < maxCount, default 1); reshow a
  changed tip by bumping the id (".v2" convention, caller's job).
- Defers showing until the target has non-empty, visible bounds (watches
  both Show and Resize).
- Single process-wide queue: only one tooltip shows at a time, closing
  one advances to the next.
- isFirstRunAfterUpgrade() gates post-upgrade tips via a stored
  "aide/gotit/previous/run" build stamp vs the app version.

Verified with dev-unix-static-debug (warnings-as-errors, ASAN+UBSAN,
clang-tidy, cppcheck); full suite green (809 assertions, 166 test
cases). Not blocked by #149 — unblocked per the map.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds aide::gui::NotificationsSettingsPage at group "Notifications",
registered beside Keymap/Appearance in the SettingsDialog tree. A
QTableView backed by NotificationGroupTableModel lists one row per
registered NotificationGroup (Group / Popup type / Log), with a combo
delegate for the popup type column. Do Not Disturb and balloon
placement sit above the table as global controls. matches()/score()/
highlight() are overridden to search the model's rows in addition to
the default child-widget scan, since table rows aren't child widgets.

NotificationManagerInterface gains groups() to enumerate the group
registry, needed to build the table.

Closes #154.
Consolidate the notification settings key format (doNotDisturb,
balloonPlacement, <group-id>.displayType) into a single header instead
of three independent copies in NotificationManager,
NotificationBalloonHost and NotificationsSettingsPage, so the locked
key format is defined exactly once.

Also nest the notification settings page under "Appearance & Behavior"
in the settings tree, and show both the group's display name and its
HierarchicalId in the Group column instead of hiding the id behind a
tooltip only.
Build the notification-view widget per #148's design lock: a plain
embeddable QWidget (not a docked tool window) showing the log as a flat,
newest-first list. NotificationLogModel (core) observes the manager's
notificationPosted/Removed/Updated/notificationsCleared signals via a
dynamic_cast<QObject*> to the concrete NotificationManager, since the
interface itself carries none (#145). NotificationLogItemDelegate paints
each card (severity stripe/icon, bold title, absolute timestamp, body,
up to 2 inline actions + "More" overflow, per-item settings/mute menu)
and hit-tests the same rects in editorEvent(), reusing the placeholder
severity palette pending #157. Read state stays boolean per #148's
amendment: the view calls markRead() on show and on every post while
visible; cards render uniform, no per-entry styling.

BREAKING CHANGE: aide::Notification (aide/include/aide/notification.hpp)
now declares Q_DECLARE_METATYPE(aide::Notification), changing the public
header's ABI surface for downstream consumers.

Closes #155.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Builds the shared notification visual language locked in #156: full-colour
SVG glyphs for each NotificationType (dialog-information, emblem-success,
dialog-warning, dialog-error) in both aide-light and aide-dark icon themes
under scalable/status/, and a theme-aware AppearanceManager::severityColor()
accessor returning the matching stripe colour. Consumers (banner #150,
balloon #152, notification-view #155) keep placeholder icons until they
migrate.

Closes #157

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Move `MainWindowInterface` onto the framework's public include path
and add `addBanner`/`removeBanner` so consumer code can reach the
editor-banner mount point through `Application::mainWindow()` without
touching internal `AideGui` headers. Source-compatible: the interface
still derives `QMainWindow`, so every existing caller keeps compiling.

Resolves #162, unblocks the notification-subsystem wayfinder map (#142).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ation group

setSearchPattern() re-emitted dataChanged even when the pattern was
unchanged. That signal is wired to onUserChangedAGuiElement(), which
clears the highlight by calling setSearchPattern("") again, so every
GUI-change notification retriggered itself and blew the stack.

Guard the setter so a redundant call is a no-op.
Balloons positioned themselves against the primary screen's geometry,
so "bottom right" meant the desktop corner rather than the app
window's corner, and they ignored the window moving, resizing, or
minimizing.

Also drop Qt::WindowStaysOnTopHint and instead parent each balloon to
the main window: that hint floated the balloon above every window on
the desktop, not just aide's own, so Alt+Tab to another app left the
balloon stranded on top of it. A transient child window is kept above
only its owner by the window manager, and is raised, moved, and
minimized together with it.

NotificationBalloonHost now positions balloons against the anchor
widget's frameGeometry(), and installs an event filter on it to
relayout on move/resize and to hide/restore balloons in step with the
window's minimize state.
The balloon panel background was a fixed pastel tint per severity, so
it stayed light even under a dark theme while the label text followed
the app's (light) theme color, making it unreadable.

Derive the background from the app's own QPalette::Window color
instead, lightened under a dark theme and darkened under a light one:
this keeps it high-contrast against the app's text color in both
themes while still reading as a distinct, raised surface. Severity is
now conveyed by the stripe and icon alone.
Same class of bugs as NotificationBalloon: the body used
QPalette::ToolTipBase for its fill, which platform themes frequently
don't honour for a custom-painted popup, leaving it unreadable in one
theme; and Qt::WindowStaysOnTopHint floated it above every window on
the desktop rather than just its target's, so it stayed open and
visible across Alt+Tab and app minimize instead of following its
owner.

Derive the surface color from the app's own Window color instead
(paired with an explicit WindowText override so labels stay
contrasting), and reparent to the target's window in showGotIt() with
WindowStaysOnTopHint dropped, so it becomes a proper transient child.
The event filter that already tracked the target's window to detect
when it becomes visible now stays installed for the tooltip's whole
life, repositioning on move/resize and hiding/restoring in step with
minimize - mirroring NotificationBalloonHost's anchor handling.
…nner

Replaces the derived stripe-color darkening with fixed, designer-
specified dark-theme backgrounds: info #263141, success #253327,
warning #3a3320, error #3a2626.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
HierarchicalId stores raw const char* levels rather than owning copies.
GotItTooltip built settings keys from m_id.c_str() (a heap string, not
a literal), and MockSettings kept that HierarchicalId as a long-lived
map key — once the tooltip was destroyed the key dangled, causing a
heap-use-after-free the next time a comparison touched it (only caught
under ASAN in CI).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
mrpilot2 and others added 6 commits July 21, 2026 01:14
Adds a "Demo" menu (before "Help") with a "Notifications" action opening
a launcher dialog. Its "Balloon" section registers two demo
NotificationGroups at startup (Balloon/StickyBalloon) and posts real
notifications through NotificationManager::post() to exercise info,
success, warning, error, multi-action and sticky balloons.

Closes #164
Code review flagged four near-identical connect() blocks for the
Balloon section's Info/Success/Warning/Error buttons; a small
per-button table removes the duplication.
…ncher

Turns the manual acceptance-criteria walkthrough for #164 (Info/Success/
Warning/Error/2-action/Sticky buttons posting through
NotificationManagerInterface) into a Catch2 + QTest test, run via ctest
alongside the rest of the suite, using a fake NotificationManagerInterface
to capture posted notifications without a real balloon host.

Kept as a separate ut.aide.demo executable under demo/tests rather than
folded into tests/ut/ut.aide, since tests/ is designed to build standalone
against an installed Aide package without demo/ present.
Closes #165. Adds a "Got it" QGroupBox to NotificationLauncherDialog
with a sample anchor button, a Below/Above/Right position selector, a
Show button that raises a real GotItTooltip against
AideSettingsProvider::unversionableSettings(), and a "Reset seen flag"
button for repeatable evaluation. Links Aide::AideWidgets into the
demo build, the first section that needs it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a "Dialog banner" section with an "Open dialog banner demo"
button that shows a new NotificationDemoDialog (AideDialog subclass)
whose Info/Success/Warning/Error buttons drive showBanner()/
clearBanner() on itself. Closes #166.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Closes #167. Info/Success/Warning/Error buttons in the launcher's
"Editor banner" section call addBanner() via the MainWindowInterface
now exposed by Application::mainWindow(), tracking the returned
Banner* pointers so "Clear all" can remove every stacked banner.
@mrpilot2
mrpilot2 force-pushed the develop+notification_system branch from 9c09a27 to c904f0a Compare July 20, 2026 23:15
mrpilot2 and others added 7 commits July 21, 2026 01:44
AideDialog was missing AIDEWIDGETS_EXPORT, unlike its sibling Banner.
On Windows shared-linkage builds this left staticMetaObject and the
vtable unexported from the DLL, so any downstream consumer of a
QObject-derived AideDialog subclass (e.g. the demo's
NotificationDemoDialog) failed to link with LNK2001.
Fill translated strings for de_DE, zh_CN, hi_IN, es_ES, fr_FR, ar_SA and sv
across the notification system UI (banners, balloons, settings page) plus
demo launcher strings; en marked finished with empty translations per
existing convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add reference pages for the notification surfaces (manager, balloons,
log view, banners, "Got it" tooltip, settings page) and for
ApplicationConfig's feature toggles, and wire both into the nav.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
NotificationBalloon and GotItTooltip were separate Qt::Tool top-level
windows positioned via absolute screen coordinates. Wayland gives clients
no global coordinate space and ignores/reinterprets such requests for
toplevels, so the compositor placed them near whatever window currently
had focus instead of the intended anchor (e.g. a balloon anchored to the
main window showing inside an unrelated dialog on top of it).

Both now stay plain child widgets of their anchor, positioned in its
local coordinate space, which needs no protocol-level placement and works
identically on X11 and Wayland.

Also fixes GotItTooltip sizing too small for wrapped body text:
sizeHint() measured height against the layout's pre-resize width instead
of the fixed TOOLTIP_WIDTH about to be applied; now uses
layout()->totalHeightForWidth() at that width.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Broke in fd4d20b: GotItTooltip is now reparented to the target's window
instead of staying a top-level Qt::Tool, so it no longer shows up in
QApplication::topLevelWidgets(). Search the dialog's children instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
clang-tidy (ci-static preset) flagged modernize-use-designated-initializers
on notification palette/aggregate structs and modernize-use-ranges on
std::find/std::find_if/std::reverse calls across the new notification code.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
MainWindow now owns a NotificationView (#155), created via
ApplicationBuilder::createNotificationLogView() and exposed through
MainWindowInterface::notificationLogView(); its "Notification
Settings..." overflow entries are wired internally to open the
Settings dialog with the built-in Notifications page pre-selected
(new ShowSettingsDialogController::showSettingsDialog(HierarchicalId)
overload), so consumers only decide where to place the widget.

Demo places it in a QSplitter on the right of the main window, behind
a checkable View menu toggle whose visibility/width persist via
QSettings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mrpilot2
mrpilot2 force-pushed the develop+notification_system branch from e564d68 to e3c33d5 Compare July 21, 2026 20:38
cppcheck can't see that manager.post() between the two liveBalloonCount()
calls changes the live count, so it flags the equality check as always
true.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mrpilot2
mrpilot2 merged commit b2af991 into develop Jul 22, 2026
55 of 58 checks 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