Skip to content

Commit 5a63f29

Browse files
authored
implement status icon blinking (#714)
Add blinking behavior to the status icon when in blinking mode and unread messages are present, and stop blinking when the window becomes active. New Features: - Introduce a blinking status icon mode that alternates between normal and reverse icons for unread messages on macOS and Linux. - Automatically stop the blinking status icon when the application window becomes active. Enhancements: - Ensure timers and icon resources for status indicators are properly cleaned up when indicators are destroyed or modes change.
1 parent 60cd410 commit 5a63f29

20 files changed

Lines changed: 255 additions & 41 deletions
1000 Bytes
Loading
1.61 KB
Loading
1.83 KB
Loading
2.84 KB
Loading
5.36 KB
Loading
7.72 KB
Loading
7.72 KB
Loading

src/config.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define __LOCALE_PATH "@SHARE_DIR@/locale"
1212
#define __SOUND_PATH "@SHARE_IPTUX_DIR@/sound"
1313
#define __UI_PATH "@SHARE_IPTUX_DIR@/ui"
14+
#define __ICON_PATH "@SHARE_DIR@/icons/hicolor"
1415

1516
#define IPTUX_PATH "/iptux"
1617
#define LOG_PATH "/iptux/log"

src/iptux/AppIndicator.cpp

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44
#include <glib/gi18n.h>
55
#include <libayatana-appindicator/app-indicator.h>
66

7+
#include "iptux-utils/output.h"
8+
79
namespace iptux {
810

911
class IptuxAppIndicatorPrivate {
1012
public:
1113
IptuxAppIndicatorPrivate(IptuxAppIndicator* owner) : owner(owner) {}
1214
~IptuxAppIndicatorPrivate() {
15+
if (blinkTimerId) {
16+
g_source_remove(blinkTimerId);
17+
}
1318
if (indicator) {
1419
g_object_unref(indicator);
1520
}
@@ -22,12 +27,49 @@ class IptuxAppIndicatorPrivate {
2227
GtkBuilder* menuBuilder;
2328
StatusIconMode mode = STATUS_ICON_MODE_NORMAL;
2429
int unreadCount = 0;
30+
guint blinkTimerId = 0;
31+
bool blinkState = false;
2532

2633
static void onScrollEvent(IptuxAppIndicatorPrivate* self) {
2734
self->owner->sigActivateMainWindow.emit();
2835
}
2936
};
3037

38+
static gboolean blinkTimerCallback(gpointer data) {
39+
auto priv = static_cast<IptuxAppIndicatorPrivate*>(data);
40+
priv->blinkState = !priv->blinkState;
41+
if (priv->blinkState) {
42+
LOG_DEBUG("blinkTimerCallback: switching to reverse icon");
43+
app_indicator_set_icon_full(priv->indicator, "iptux-icon-reverse",
44+
"iptux-icon-reverse");
45+
} else {
46+
LOG_DEBUG("blinkTimerCallback: switching to normal icon");
47+
app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon");
48+
}
49+
return G_SOURCE_CONTINUE;
50+
}
51+
52+
static void startBlinkTimer(IptuxAppIndicatorPrivate* priv) {
53+
if (priv->blinkTimerId) {
54+
LOG_DEBUG("startBlinkTimer: timer already running (id=%u)", priv->blinkTimerId);
55+
return;
56+
}
57+
priv->blinkState = false;
58+
priv->blinkTimerId = g_timeout_add(500, blinkTimerCallback, priv);
59+
LOG_DEBUG("startBlinkTimer: blinking started (timerId=%u)", priv->blinkTimerId);
60+
}
61+
62+
static void stopBlinkTimer(IptuxAppIndicatorPrivate* priv) {
63+
if (priv->blinkTimerId) {
64+
LOG_DEBUG("stopBlinkTimer: blinking stopped (timerId=%u)", priv->blinkTimerId);
65+
g_source_remove(priv->blinkTimerId);
66+
priv->blinkTimerId = 0;
67+
} else {
68+
LOG_DEBUG("stopBlinkTimer: no timer was running");
69+
}
70+
priv->blinkState = false;
71+
}
72+
3173
IptuxAppIndicator::IptuxAppIndicator(GActionGroup* action_group) {
3274
this->priv = std::make_shared<IptuxAppIndicatorPrivate>(this);
3375

@@ -42,7 +84,7 @@ IptuxAppIndicator::IptuxAppIndicator(GActionGroup* action_group) {
4284
app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_ACTIVE);
4385
app_indicator_set_attention_icon_full(priv->indicator, "iptux-attention",
4486
"iptux-attention");
45-
87+
app_indicator_set_icon_theme_path(priv->indicator, __ICON_PATH);
4688
app_indicator_set_title(priv->indicator, _("Iptux"));
4789

4890
priv->menuBuilder =
@@ -59,8 +101,24 @@ IptuxAppIndicator::IptuxAppIndicator(GActionGroup* action_group) {
59101
}
60102

61103
void IptuxAppIndicator::SetUnreadCount(int i) {
104+
LOG_DEBUG("SetUnreadCount: count=%d, mode=%d", i, priv->mode);
62105
priv->unreadCount = i;
63-
if (priv->mode == STATUS_ICON_MODE_NONE) return;
106+
if (priv->mode == STATUS_ICON_MODE_NONE) {
107+
LOG_DEBUG("SetUnreadCount: early return (mode=NONE)");
108+
return;
109+
}
110+
111+
if (priv->mode == STATUS_ICON_MODE_BLINKING) {
112+
if (i > 0) {
113+
startBlinkTimer(priv.get());
114+
} else {
115+
stopBlinkTimer(priv.get());
116+
app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon");
117+
app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_ACTIVE);
118+
}
119+
return;
120+
}
121+
64122
if (i > 0) {
65123
app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_ATTENTION);
66124
} else {
@@ -69,12 +127,28 @@ void IptuxAppIndicator::SetUnreadCount(int i) {
69127
}
70128

71129
void IptuxAppIndicator::SetMode(StatusIconMode mode) {
130+
LOG_DEBUG("SetMode: mode=%d (old=%d)", mode, priv->mode);
131+
StatusIconMode oldMode = priv->mode;
72132
priv->mode = mode;
133+
134+
if (oldMode == STATUS_ICON_MODE_BLINKING) {
135+
stopBlinkTimer(priv.get());
136+
app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon");
137+
}
138+
73139
if (mode == STATUS_ICON_MODE_NONE) {
74140
app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_PASSIVE);
75141
} else {
76142
SetUnreadCount(priv->unreadCount);
77143
}
78144
}
79145

146+
void IptuxAppIndicator::StopBlinking() {
147+
LOG_DEBUG("StopBlinking called");
148+
stopBlinkTimer(priv.get());
149+
app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon");
150+
if (priv->mode == STATUS_ICON_MODE_NONE) return;
151+
app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_ACTIVE);
152+
}
153+
80154
} // namespace iptux

src/iptux/AppIndicator.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ class IptuxAppIndicator {
1414
IptuxAppIndicator(GActionGroup* action_group);
1515
void SetUnreadCount(int count);
1616
void SetMode(StatusIconMode mode);
17+
void StopBlinking();
1718

1819
sigc::signal<void> sigActivateMainWindow;
1920

2021
private:
2122
std::shared_ptr<IptuxAppIndicatorPrivate> priv;
2223
};
24+
#ifdef __APPLE__
25+
void ActivateApplication();
26+
#endif
27+
2328
} // namespace iptux

0 commit comments

Comments
 (0)