mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
feat(bar): add unread indicator to notification widget
This commit is contained in:
+11
-3
@@ -21,10 +21,18 @@ void signal_handler(int signum) {
|
||||
Application::Application() : m_internalNotifications(m_manager) {
|
||||
logInfo("noctalia hello");
|
||||
|
||||
m_manager.setEventCallback([](const Notification& n, NotificationEvent event) {
|
||||
const char* kind = (event == NotificationEvent::Added) ? "added" : "updated";
|
||||
m_manager.setEventCallback([this](const Notification& n, NotificationEvent event) {
|
||||
const char* kind = "updated";
|
||||
if (event == NotificationEvent::Added) {
|
||||
kind = "added";
|
||||
} else if (event == NotificationEvent::Closed) {
|
||||
kind = "closed";
|
||||
}
|
||||
const char* origin = (n.origin == NotificationOrigin::Internal) ? "internal" : "external";
|
||||
logDebug("notification {} id={} origin={}", kind, n.id, origin);
|
||||
|
||||
// Keep bar widgets in sync with notification state changes.
|
||||
m_bar.onWorkspaceChange();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -69,7 +77,7 @@ void Application::run() {
|
||||
m_wallpaper.initialize(m_wayland, &m_configService, &m_stateService);
|
||||
|
||||
// Initialize bar (top layer)
|
||||
m_bar.initialize(m_wayland, &m_configService, &m_timeService);
|
||||
m_bar.initialize(m_wayland, &m_configService, &m_timeService, &m_manager);
|
||||
|
||||
try {
|
||||
m_systemMonitor = std::make_unique<SystemMonitorService>();
|
||||
|
||||
@@ -122,6 +122,7 @@ bool NotificationManager::close(uint32_t id, CloseReason reason) {
|
||||
}
|
||||
|
||||
const size_t index = it->second;
|
||||
const Notification closed = m_notifications[index];
|
||||
const char* reason_str = (reason == CloseReason::Expired) ? "expired"
|
||||
: (reason == CloseReason::Dismissed) ? "dismissed"
|
||||
: "closed";
|
||||
@@ -134,6 +135,10 @@ bool NotificationManager::close(uint32_t id, CloseReason reason) {
|
||||
m_id_to_index[m_notifications[i].id] = i;
|
||||
}
|
||||
|
||||
if (m_event_callback) {
|
||||
m_event_callback(closed, NotificationEvent::Closed);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
enum class NotificationEvent {
|
||||
Added,
|
||||
Updated,
|
||||
Closed,
|
||||
};
|
||||
|
||||
class NotificationManager {
|
||||
|
||||
+5
-3
@@ -34,12 +34,14 @@ std::uint32_t positionToAnchor(const std::string& position) {
|
||||
|
||||
Bar::Bar() = default;
|
||||
|
||||
bool Bar::initialize(WaylandConnection& wayland, ConfigService* config, TimeService* timeService) {
|
||||
bool Bar::initialize(WaylandConnection& wayland, ConfigService* config, TimeService* timeService,
|
||||
NotificationManager* notifications) {
|
||||
m_wayland = &wayland;
|
||||
m_config = config;
|
||||
m_time = timeService;
|
||||
m_notifications = notifications;
|
||||
|
||||
m_widgetFactory = std::make_unique<WidgetFactory>(*m_wayland, m_time, m_config->config());
|
||||
m_widgetFactory = std::make_unique<WidgetFactory>(*m_wayland, m_time, m_config->config(), m_notifications);
|
||||
|
||||
if (timeService != nullptr) {
|
||||
timeService->setTickSecondCallback([this]() {
|
||||
@@ -66,7 +68,7 @@ bool Bar::initialize(WaylandConnection& wayland, ConfigService* config, TimeServ
|
||||
|
||||
void Bar::reload() {
|
||||
logInfo("bar: reloading config");
|
||||
m_widgetFactory = std::make_unique<WidgetFactory>(*m_wayland, m_time, m_config->config());
|
||||
m_widgetFactory = std::make_unique<WidgetFactory>(*m_wayland, m_time, m_config->config(), m_notifications);
|
||||
m_instances.clear();
|
||||
m_surfaceMap.clear();
|
||||
m_hoveredInstance = nullptr;
|
||||
|
||||
+4
-1
@@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
class ConfigService;
|
||||
class NotificationManager;
|
||||
class TimeService;
|
||||
class WaylandConnection;
|
||||
struct PointerEvent;
|
||||
@@ -17,7 +18,8 @@ class Bar {
|
||||
public:
|
||||
Bar();
|
||||
|
||||
bool initialize(WaylandConnection& wayland, ConfigService* config, TimeService* timeService);
|
||||
bool initialize(WaylandConnection& wayland, ConfigService* config, TimeService* timeService,
|
||||
NotificationManager* notifications);
|
||||
void reload();
|
||||
void closeAllInstances();
|
||||
void onOutputChange();
|
||||
@@ -37,6 +39,7 @@ private:
|
||||
WaylandConnection* m_wayland = nullptr;
|
||||
ConfigService* m_config = nullptr;
|
||||
TimeService* m_time = nullptr;
|
||||
NotificationManager* m_notifications = nullptr;
|
||||
std::unique_ptr<WidgetFactory> m_widgetFactory;
|
||||
std::vector<std::unique_ptr<BarInstance>> m_instances;
|
||||
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
#include "config/ConfigService.h"
|
||||
#include "core/Log.h"
|
||||
#include "notification/NotificationManager.h"
|
||||
#include "shell/widgets/ClockWidget.h"
|
||||
#include "shell/widgets/NotificationWidget.h"
|
||||
#include "shell/widgets/SpacerWidget.h"
|
||||
#include "shell/widgets/WorkspacesWidget.h"
|
||||
|
||||
WidgetFactory::WidgetFactory(WaylandConnection& wayland, TimeService* time, const Config& config)
|
||||
: m_wayland(wayland), m_time(time), m_config(config) {}
|
||||
WidgetFactory::WidgetFactory(WaylandConnection& wayland, TimeService* time, const Config& config,
|
||||
NotificationManager* notifications)
|
||||
: m_wayland(wayland), m_time(time), m_config(config), m_notifications(notifications) {}
|
||||
|
||||
std::unique_ptr<Widget> WidgetFactory::create(const std::string& name, wl_output* output) const {
|
||||
if (name == "clock") {
|
||||
@@ -24,7 +26,7 @@ std::unique_ptr<Widget> WidgetFactory::create(const std::string& name, wl_output
|
||||
}
|
||||
|
||||
if (name == "notifications") {
|
||||
return std::make_unique<NotificationWidget>();
|
||||
return std::make_unique<NotificationWidget>(m_notifications);
|
||||
}
|
||||
|
||||
if (name == "spacer") {
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
#include <string>
|
||||
|
||||
struct Config;
|
||||
class NotificationManager;
|
||||
struct wl_output;
|
||||
class TimeService;
|
||||
class WaylandConnection;
|
||||
|
||||
class WidgetFactory {
|
||||
public:
|
||||
WidgetFactory(WaylandConnection& wayland, TimeService* time, const Config& config);
|
||||
WidgetFactory(WaylandConnection& wayland, TimeService* time, const Config& config,
|
||||
NotificationManager* notifications);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Widget> create(const std::string& name, wl_output* output) const;
|
||||
|
||||
@@ -20,4 +22,5 @@ private:
|
||||
WaylandConnection& m_wayland;
|
||||
TimeService* m_time;
|
||||
const Config& m_config;
|
||||
NotificationManager* m_notifications;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,75 @@
|
||||
#include "shell/widgets/NotificationWidget.h"
|
||||
|
||||
#include "notification/NotificationManager.h"
|
||||
#include "render/programs/RoundedRectProgram.h"
|
||||
#include "render/scene/RectNode.h"
|
||||
#include "ui/controls/Icon.h"
|
||||
#include "ui/style/Palette.h"
|
||||
#include "ui/style/Style.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
NotificationWidget::NotificationWidget(NotificationManager* manager) : m_manager(manager) {}
|
||||
|
||||
void NotificationWidget::create(Renderer& renderer) {
|
||||
auto root = std::make_unique<Node>();
|
||||
|
||||
auto icon = std::make_unique<Icon>();
|
||||
icon->setIcon("bell");
|
||||
icon->setColor(palette.onSurface);
|
||||
m_icon = icon.get();
|
||||
m_root = std::move(icon);
|
||||
m_icon->measure(renderer);
|
||||
root->addChild(std::move(icon));
|
||||
|
||||
auto dot = std::make_unique<RectNode>();
|
||||
auto dotStyle = dot->style();
|
||||
dotStyle.fill = palette.primary;
|
||||
dotStyle.border = palette.primary;
|
||||
dotStyle.fillMode = FillMode::Solid;
|
||||
dotStyle.radius = 3.0f;
|
||||
dotStyle.softness = 1.0f;
|
||||
dotStyle.borderWidth = 0.0f;
|
||||
dot->setStyle(dotStyle);
|
||||
dot->setVisible(false);
|
||||
m_dot = static_cast<RectNode*>(root->addChild(std::move(dot)));
|
||||
|
||||
m_root = std::move(root);
|
||||
refreshIndicatorState();
|
||||
layout(renderer, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void NotificationWidget::layout(Renderer& renderer, float /*containerWidth*/, float /*containerHeight*/) {
|
||||
auto* rootNode = root();
|
||||
if (m_icon == nullptr || rootNode == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_icon->measure(renderer);
|
||||
m_icon->setPosition(0.0f, 0.0f);
|
||||
rootNode->setSize(m_icon->width(), m_icon->height());
|
||||
|
||||
if (m_dot != nullptr && m_dot->visible()) {
|
||||
constexpr float kDotSize = 5.0f;
|
||||
const float dotX = std::max(0.0f, m_icon->width() - kDotSize + 1.0f);
|
||||
const float dotY = -1.0f;
|
||||
m_dot->setPosition(dotX, dotY);
|
||||
m_dot->setSize(kDotSize, kDotSize);
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationWidget::update(Renderer& renderer) {
|
||||
refreshIndicatorState();
|
||||
Widget::update(renderer);
|
||||
}
|
||||
|
||||
void NotificationWidget::refreshIndicatorState() {
|
||||
const bool hasNotifications = (m_manager != nullptr) && !m_manager->all().empty();
|
||||
if (hasNotifications == m_hasNotifications) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_hasNotifications = hasNotifications;
|
||||
if (m_dot != nullptr) {
|
||||
m_dot->setVisible(m_hasNotifications);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,22 @@
|
||||
#include "shell/Widget.h"
|
||||
|
||||
class Icon;
|
||||
class NotificationManager;
|
||||
class RectNode;
|
||||
|
||||
class NotificationWidget : public Widget {
|
||||
public:
|
||||
explicit NotificationWidget(NotificationManager* manager);
|
||||
|
||||
void create(Renderer& renderer) override;
|
||||
void layout(Renderer& renderer, float containerWidth, float containerHeight) override;
|
||||
void update(Renderer& renderer) override;
|
||||
|
||||
private:
|
||||
void refreshIndicatorState();
|
||||
|
||||
NotificationManager* m_manager = nullptr;
|
||||
Icon* m_icon = nullptr;
|
||||
RectNode* m_dot = nullptr;
|
||||
bool m_hasNotifications = false;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user