fluent-tray
Fluent Design-based GUI Library for System Tray Applications
Loading...
Searching...
No Matches
fluent_tray.hpp
Go to the documentation of this file.
1
9/*
10 * MIT License
11 *
12 * Copyright (c) 2024 pit-ray
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a copy
15 * of this software and associated documentation files (the "Software"), to deal
16 * in the Software without restriction, including without limitation the rights
17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 * copies of the Software, and to permit persons to whom the Software is
19 * furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included in all
22 * copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 */
32
33#ifndef _FLUENT_TRAY_HPP
34#define _FLUENT_TRAY_HPP
35
36#include <windows.h>
37
38#if defined(_MSC_VER) && _MSC_VER >= 1500
39#pragma warning(disable : 4005)
40#endif
41
42#include <ntstatus.h>
43
44#if defined(_MSC_VER) && _MSC_VER >= 1500
45#pragma warning(default : 4005)
46#endif
47
48#include <dwmapi.h>
49
50#ifndef DOXYGEN_SHOULD_SKIP_THIS
51
52#ifndef DWMWA_COLOR_DEFAULT
53#define DWMWA_WINDOW_CORNER_PREFERENCE static_cast<DWMWINDOWATTRIBUTE>(33)
54typedef enum {
55 DWMWCP_DEFAULT = 0,
56 DWMWCP_DONOTROUND = 1,
57 DWMWCP_ROUND = 2,
58 DWMWCP_ROUNDSMALL = 3
59} DWM_WINDOW_CORNER_PREFERENCE;
60#endif /* DWMWA_COLOR_DEFAULT */
61
62#if defined(DEBUG)
63#include <iostream>
64#endif
65
66#endif /* DOXYGEN_SHOULD_SKIP_THIS */
67
68#pragma comment(lib, "Dwmapi")
69
70#include <algorithm>
71#include <chrono>
72#include <cmath>
73#include <functional>
74#include <memory>
75#include <string>
76#include <vector>
77
78
83namespace fluent_tray
84{
89 namespace util
90 {
98 bool string2wstring(const std::string& str, std::wstring& wstr) {
99 if(str.empty()) {
100 wstr.clear() ;
101 return true ;
102 }
103
104 auto needed_size = MultiByteToWideChar(
105 CP_UTF8, 0,
106 str.c_str(), static_cast<int>(str.length()),
107 NULL, 0) ;
108#ifndef _FLUENT_TRAY_IGNORE_IN_TEST
109 if(needed_size <= 0) {
110 return false ;
111 }
112#endif
113
114 wstr.resize(needed_size) ;
115 if(MultiByteToWideChar(
116 CP_UTF8, 0,
117 str.c_str(), static_cast<int>(str.length()),
118 &wstr[0], static_cast<int>(wstr.length())) <= 0) {
119#ifndef _FLUENT_TRAY_IGNORE_IN_TEST
120 return false;
121#endif
122 }
123 return true ;
124 }
125
133 bool wstring2string(const std::wstring& wstr, std::string& str) {
134 if(wstr.empty()) {
135 str.clear() ;
136 return true ;
137 }
138
139 auto needed_size = WideCharToMultiByte(
140 CP_UTF8, 0,
141 wstr.c_str(), static_cast<int>(wstr.length()),
142 NULL, 0,
143 NULL, NULL) ;
144#ifndef _FLUENT_TRAY_IGNORE_IN_TEST
145 if(needed_size <= 0) {
146 return false ;
147 }
148#endif
149
150 str.resize(needed_size) ;
151 if(WideCharToMultiByte(
152 CP_UTF8, 0,
153 wstr.c_str(), static_cast<int>(wstr.length()),
154 &str[0], static_cast<int>(str.size()),
155 NULL, NULL) <= 0) {
156#ifndef _FLUENT_TRAY_IGNORE_IN_TEST
157 return false ;
158#endif
159 }
160 return true ;
161 }
162
168 inline constexpr std::size_t bit2mask(std::size_t bits) noexcept {
169 return (static_cast<std::size_t>(1) << bits) - 1 ;
170 }
171
176 template <typename Type>
177 inline constexpr int type2bit() noexcept {
178 return static_cast<int>(sizeof(Type) * CHAR_BIT) ;
179 }
180
189 template <typename InType, typename OutType>
190 inline void split_bits(InType input, OutType& upper, OutType& lower) noexcept {
191 static constexpr auto bits = type2bit<OutType>() ;
192 static const auto lower_mask = util::bit2mask(bits) ;
193
194 upper = static_cast<OutType>(reinterpret_cast<std::size_t>(input) >> bits) ;
195 lower = static_cast<OutType>(reinterpret_cast<std::size_t>(input) & lower_mask) ;
196 }
197
205 template <typename InType, typename OutType>
207 static constexpr auto bits = type2bit<InType>() ;
208 static const auto lower_mask = util::bit2mask(bits) ;
209
210 auto out_upper = static_cast<std::size_t>(upper) << bits ;
211 auto out_lower = static_cast<std::size_t>(lower) & lower_mask ;
212 out = reinterpret_cast<OutType>(out_upper | out_lower) ;
213 }
219 inline unsigned char rgb2gray(const COLORREF& rgb) {
220 auto r = GetRValue(rgb) ;
221 auto g = GetGValue(rgb) ;
222 auto b = GetBValue(rgb) ;
223 return static_cast<unsigned char>(0.2126 * r + 0.7152 * g + 0.0722 * b) ;
224 }
225
231 inline bool exists(const std::wstring& path) {
232 struct _stat buffer ;
233 return _wstat(path.c_str(), &buffer) == 0 ;
234 }
235 }
236
240 enum class TrayStatus : unsigned char
241 {
243 RUNNING,
244
247
249 FAILED,
250
252 STOPPED,
253 } ;
254
259 private:
260 std::wstring label_ ;
261 HICON hicon_ ;
262
263 bool toggleable_ ;
264 bool checked_ ;
265 std::wstring checkmark_ ;
266
267 HWND hwnd_ ;
268 HMENU hmenu_ ;
269
270 bool under_line_ ;
271
272 COLORREF text_color_ ;
273 COLORREF back_color_ ;
274 COLORREF border_color_ ;
275 HBRUSH back_brush_ ;
276
277 std::function<bool(void)> callback_ ;
278 std::function<bool(void)> unchecked_callback_ ;
279
280 public:
288 explicit FluentMenu(
289 bool toggleable=false,
290 const std::function<bool(void)>& callback=[] {return true ;},
291 const std::function<bool(void)>& unchecked_callback=[] {return true ;})
292 : label_(),
293 hicon_(NULL),
294 toggleable_(toggleable),
295 checked_(false),
296 checkmark_(),
297 hwnd_(NULL),
298 hmenu_(NULL),
299 under_line_(false),
300 text_color_(RGB(0, 0, 0)),
301 back_color_(RGB(255, 255, 255)),
302 border_color_(RGB(128, 128, 128)),
303 back_brush_(NULL),
304 callback_(callback),
305 unchecked_callback_(unchecked_callback)
306 {}
307
308 FluentMenu(const FluentMenu&) = default ;
309 FluentMenu& operator=(const FluentMenu&) = default ;
310
311 FluentMenu(FluentMenu&&) = default ;
313
314 ~FluentMenu() noexcept {
315 if(back_brush_) {
316 DeleteObject(back_brush_) ;
317 }
318 }
319
331 HINSTANCE hinstance,
332 HWND parent_hwnd,
333 std::size_t id,
334 const std::string& label_text="",
335 const std::string& icon_path="",
336 const std::string& checkmark="✓") {
337
338 // Convert strings to the wide-strings
339 if(!util::string2wstring(label_text, label_)) {
340 return false ;
341 }
342 if(!util::string2wstring(checkmark, checkmark_)) {
343 return false ;
344 }
345
346 auto style = WS_CHILD | WS_VISIBLE | BS_FLAT | BS_LEFT | BS_OWNERDRAW ;
347
348 hmenu_ = reinterpret_cast<HMENU>(id) ;
349 hwnd_ = CreateWindowW(
350 TEXT("BUTTON"), label_.c_str(), style,
351 0, 0, 100, 100,
352 parent_hwnd, hmenu_,
353 hinstance, NULL) ;
354 if(!hwnd_) {
355 return false ;
356 }
357
358 // Hide dash lines when selecting.
359 SendMessageW(
360 hwnd_, WM_CHANGEUISTATE,
361 WPARAM(MAKELONG(UIS_SET, UISF_HIDEFOCUS)), 0) ;
362
363 if(!icon_path.empty()) {
364 std::wstring icon_path_wide ;
365 if(!util::string2wstring(icon_path, icon_path_wide)) {
366 return false ;
367 }
368
369 if(!util::exists(icon_path_wide)) {
370 return false ;
371 }
372
373 hicon_ = static_cast<HICON>(LoadImageW(
374 NULL, icon_path_wide.c_str(),
375 IMAGE_ICON, 0, 0, LR_LOADFROMFILE)) ;
376 if(!hicon_) {
377 return false ;
378 }
379 }
380
381 return true ;
382 }
383
390 if(toggleable_) {
391 checked_ = !checked_ ;
392 if(!checked_) {
393 return unchecked_callback_() ;
394 }
395 }
396 return callback_() ;
397 }
398
403 void check() noexcept {
404 if(toggleable_) {
405 checked_ = true ;
406 }
407 }
408
413 void uncheck() noexcept {
414 if(toggleable_) {
415 checked_ = false ;
416 }
417 }
418
423 bool is_checked() const noexcept {
424 if(!toggleable_) {
425 return false ;
426 }
427 return checked_ ;
428 }
429
434 bool is_toggleable() const noexcept {
435 return toggleable_ ;
436 }
437
442 HWND window_handle() const noexcept {
443 return hwnd_ ;
444 }
445
450 HMENU menu_handle() const noexcept {
451 return hmenu_ ;
452 }
453
458 std::size_t id() const noexcept {
459 return reinterpret_cast<std::size_t>(hmenu_) ;
460 }
466 bool get_label(std::string& str) const {
467 return util::wstring2string(label_, str) ;
468 }
469
473 void show_separator_line() noexcept {
474 under_line_ = true ;
475 }
476
480 void hide_separator_line() noexcept {
481 under_line_ = false ;
482 }
483
493 const COLORREF& text_color=CLR_INVALID,
494 const COLORREF& back_color=CLR_INVALID,
495 const COLORREF& border_color=CLR_INVALID) noexcept {
496 if(text_color != CLR_INVALID) {
497 text_color_ = text_color ;
498 }
499 if(back_color != CLR_INVALID) {
500 back_color_ = back_color ;
501 }
502 if(border_color != CLR_INVALID) {
503 border_color_ = border_color ;
504 }
505
506 // Create brush handle to draw a background of window.
507 if(back_brush_) {
508 // Release old handle.
509 if(!DeleteObject(back_brush_)) {
510 return false ;
511 }
512 }
513 back_brush_ = CreateSolidBrush(back_color_) ;
514 if(back_brush_ == NULL) {
515 return false ;
516 }
517
518 return true ;
519 }
520
528 bool draw_menu(LPDRAWITEMSTRUCT info, HFONT font) const {
529 if(SetTextColor(info->hDC, text_color_) == CLR_INVALID) {
530 return false ;
531 }
532
533 if(SetBkColor(info->hDC, back_color_) == CLR_INVALID) {
534 return false ;
535 }
536
537 if(font) {
538 if(!SelectObject(info->hDC, font)) {
539 return false ;
540 }
541 }
542
543 LONG checkmark_size, label_height, icon_size, margin ;
544 if(!calculate_layouts(
545 info->hDC,
546 checkmark_size, label_height, icon_size, margin)) {
547 return false ;
548 }
549
550 auto& rect = info->rcItem ;
551
552 auto y_center = rect.top + (rect.bottom - rect.top) / 2 ;
553 auto x = rect.left + margin ;
554
555 if(toggleable_ && checked_) {
556 if(!TextOutW(
557 info->hDC, x, y_center - label_height / 2, checkmark_.c_str(),
558 static_cast<int>(checkmark_.length()))) {
559 return false ;
560 }
561 }
562 x += checkmark_size + margin ;
563
564 if(hicon_) {
565 if(!DrawIconEx(
566 info->hDC, x, y_center - SM_CYICON / 2, hicon_,
567 icon_size, icon_size, 0, NULL, DI_NORMAL)) {
568 return false ;
569 }
570 }
571 x += icon_size + margin ;
572
573 if(!TextOutW(
574 info->hDC, x, y_center - label_height / 2, label_.c_str(),
575 static_cast<int>(label_.length()))) {
576 return false ;
577 }
578
579 if(under_line_) {
580 auto original_obj = SelectObject(info->hDC, GetStockObject(DC_PEN)) ;
581 if(SetDCPenColor(info->hDC, border_color_) == CLR_INVALID) {
582 return false ;
583 }
584
585 auto lx = info->rcItem.left ;
586 auto ly = info->rcItem.bottom - 1 ;
587 auto rx = info->rcItem.right ;
588 auto ry = ly + 1 ;
589 if(!Rectangle(info->hDC, lx, ly, rx, ry)) {
590 return false ;
591 }
592
593 if(!SelectObject(info->hDC, original_obj)) {
594 return false ;
595 }
596 }
597
598 return true ;
599 }
605 HBRUSH background_brush() const noexcept {
606 return back_brush_ ;
607 }
614 bool calculate_required_dims(HFONT font, SIZE& size) const {
615 auto hdc = GetDC(hwnd_) ;
616 if(font) {
617 if(!SelectObject(hdc, font)) {
618 return false ;
619 }
620 }
621
622 if(!GetTextExtentPoint32W(
623 hdc, label_.c_str(),
624 static_cast<int>(label_.length()), &size)) {
625 return false ;
626 }
627
628 LONG checkmark_size, label_height, icon_size, margin ;
629 if(!calculate_layouts(
630 hdc, checkmark_size, label_height, icon_size, margin)) {
631 return false ;
632 }
633
634 size.cx += margin + checkmark_size + margin + icon_size + margin ;
635
636 return true ;
637 }
638
639
640 private:
641 bool calculate_layouts(
642 HDC hdc,
643 LONG& checkmark_size,
644 LONG& label_height,
645 LONG& icon_size,
646 LONG& margin) const {
647 SIZE size ;
648 if(!GetTextExtentPoint32W(hdc, L" ", 1, &size)) {
649 return false ;
650 }
651 checkmark_size = size.cy ;
652 margin = size.cy / 2 ;
653 label_height = size.cy ;
654 icon_size = 4 * label_height / 5 ;
655 return true ;
656 }
657
658 } ;
659
660
665 private:
666 std::wstring app_name_ ;
667
668 HINSTANCE hinstance_ ;
669 HWND hwnd_ ;
670 bool visible_ ;
671 NOTIFYICONDATAW icon_data_ ;
672
673 TrayStatus status_ ;
674
675 std::vector<FluentMenu> menus_ ;
676 std::vector<bool> status_if_focus ;
677 std::size_t next_menu_id_ ;
678 int select_index_ ;
679
680 POINT previous_mouse_pos_ ;
681
682 LONG menu_x_margin_ ;
683 LONG menu_y_margin_ ;
684
685 LONG menu_x_pad_ ;
686 LONG menu_y_pad_ ;
687
688 COLORREF text_color_ ;
689 COLORREF back_color_ ;
690 COLORREF border_color_ ;
691 unsigned char color_decay_ ;
692 HBRUSH back_brush_ ;
693 int autocolorpick_offset_ ;
694
695 LONG menu_font_size_ ;
696 HFONT font_ ;
697
698 static unsigned int message_id_ ;
699
700 public:
711 explicit FluentTray(
712 LONG menu_x_margin=5,
713 LONG menu_y_margin=5,
714 LONG menu_x_pad=10,
715 LONG menu_y_pad=5,
716 unsigned char autofadedborder_from_backcolor=10,
717 int autocolorpick_offset=5,
718 int message_id_offset=25)
719 : app_name_(),
720 hinstance_(GetModuleHandle(NULL)),
721 hwnd_(NULL),
722 visible_(false),
723 icon_data_(),
724 status_(TrayStatus::STOPPED),
725 menus_(),
726 status_if_focus(),
727 next_menu_id_(1),
728 select_index_(-1),
729 previous_mouse_pos_(),
730 menu_x_margin_(menu_x_margin),
731 menu_y_margin_(menu_y_margin),
732 menu_x_pad_(menu_x_pad),
733 menu_y_pad_(menu_y_pad),
734 text_color_(CLR_INVALID),
735 back_color_(CLR_INVALID),
736 border_color_(CLR_INVALID),
737 color_decay_(autofadedborder_from_backcolor),
738 back_brush_(NULL),
739 autocolorpick_offset_(autocolorpick_offset),
740 menu_font_size_(0),
741 font_(NULL)
742 {
743 message_id_ = WM_APP + message_id_offset ;
744 }
745
746 // Copy
747 FluentTray(const FluentTray&) = delete ;
748 FluentTray& operator=(const FluentTray&) = delete ;
749
750 // Move
751 FluentTray(FluentTray&&) = default ;
753
754 virtual ~FluentTray() noexcept {
755 if(font_ != NULL) {
756 DeleteObject(font_) ;
757 }
758 if(back_brush_ != NULL) {
759 DeleteObject(back_brush_) ;
760 }
761 }
762
772 const std::string& app_name,
773 const std::string& icon_path="",
774 unsigned char opacity=255,
775 bool round_corner=true) {
776 if(!util::string2wstring(app_name, app_name_)) {
777 return false ;
778 }
779
780 WNDCLASSW winc ;
781 winc.style = CS_HREDRAW | CS_VREDRAW ;
782 winc.lpfnWndProc = &FluentTray::callback ;
783 winc.cbClsExtra = 0 ;
784 winc.cbWndExtra = sizeof(LONG) * 2 ; // To store two-part address.
785 winc.hInstance = hinstance_ ;
786 winc.hIcon = LoadIcon(NULL, IDI_APPLICATION) ;
787 winc.hCursor = LoadCursor(NULL, IDC_ARROW) ;
788 winc.hbrBackground = GetSysColorBrush(COLOR_WINDOW) ;
789 winc.lpszMenuName = NULL ;
790 winc.lpszClassName = app_name_.c_str() ;
791
792 if(!RegisterClassW(&winc)) {
793 return false ;
794 }
795
796 hwnd_ = CreateWindowExW(
797 WS_EX_TOOLWINDOW | WS_EX_LAYERED,
798 app_name_.c_str(),
799 app_name_.c_str(),
800 WS_POPUPWINDOW,
801 0, 0, 100, 100,
802 NULL, NULL,
803 hinstance_, NULL
804 ) ;
805 if(!hwnd_) {
806 return false ;
807 }
808
809 // To access the this pointer inside the callback function,
810 // the address divide into the two part address.
811 LONG upper_addr, lower_addr ;
812 util::split_bits(this, upper_addr, lower_addr) ;
813
814 SetLastError(0) ;
815 if(!SetWindowLongW(hwnd_, 0, upper_addr) && GetLastError() != 0) {
816 return false ;
817 }
818 SetLastError(0) ;
819 if(!SetWindowLongW(hwnd_, sizeof(LONG), lower_addr) && GetLastError() != 0) {
820 return false ;
821 }
822
823 if(!SetLayeredWindowAttributes(hwnd_, 0, opacity, LWA_ALPHA)) {
824 return false ;
825 }
826
827 // Set rounded window for Windows 11 only.
828 if(round_corner) {
829#if defined(_MSC_VER) && _MSC_VER >= 1500
830 using RtlGetVersionType = NTSTATUS (WINAPI*)(PRTL_OSVERSIONINFOW) ;
831 const auto hmodule = LoadLibraryW(L"ntdll.dll") ;
832 if(!hmodule) {
833 return false ;
834 }
835
836 // cast to void* once to avoid warnings about type conversion.
837 const auto RtlGetVersion = reinterpret_cast<RtlGetVersionType>(
838 reinterpret_cast<void*>(GetProcAddress(hmodule, "RtlGetVersion"))) ;
839 if(!RtlGetVersion) {
840 FreeLibrary(hmodule) ;
841 return false ;
842 }
843
844 OSVERSIONINFOW vinfo ;
845 vinfo.dwOSVersionInfoSize = sizeof(decltype(vinfo)) ;
846 auto result = RtlGetVersion(&vinfo) ;
847 if(!FreeLibrary(hmodule)) {
848 return false ;
849 }
850
851 if(result != STATUS_SUCCESS) {
852 return false ;
853 }
854
855 // Check if the OS is Windows11
856 if(vinfo.dwBuildNumber >= 22000) {
857 auto pref = DWMWCP_ROUND ;
858 if(DwmSetWindowAttribute(
859 hwnd_,
860 DWMWA_WINDOW_CORNER_PREFERENCE,
861 &pref, sizeof(pref)) != S_OK) {
862 return false ;
863 }
864 }
865#endif // defined(_MSC_VER) && _MSC_VER >= 1500
866 }
867
868 if(!change_icon(icon_path)) {
869 return false ;
870 }
871
872 if(!set_font()) {
873 return false ;
874 }
875
876 status_ = TrayStatus::RUNNING ;
877
878 return true ;
879 }
880
892 const std::string& label_text="",
893 const std::string& icon_path="",
894 bool toggleable=false,
895 const std::string& checkmark="✓",
896 const std::function<bool(void)>& callback=[] {return true ;},
897 const std::function<bool(void)>& unchecked_callback=[] {return true ;}) {
898 FluentMenu menu(toggleable, callback, unchecked_callback) ;
899 if(!menu.create_menu(
900 hinstance_, hwnd_, next_menu_id_,
901 label_text, icon_path, checkmark)) {
902 return false ;
903 }
904
905 menus_.push_back(std::move(menu)) ;
906 status_if_focus.push_back(false) ;
907 next_menu_id_ ++ ;
908 return true ;
909 }
910
915 if(!menus_.empty()) {
916 menus_.back().show_separator_line() ;
917 }
918 }
919
924 bool update() {
925 if(status_ == TrayStatus::FAILED) {
926 return false ;
927 }
928
929 MSG msg ;
930 get_message(msg) ;
931
932 if(GetForegroundWindow() != hwnd_ && visible_) {
933 if(!hide_menu_window()) {
934 fail() ;
935 return false ;
936 }
937 }
938
939 POINT pos ;
940 if(!GetCursorPos(&pos)) {
941 return false ;
942 }
943
944 if(pos.x != previous_mouse_pos_.x || pos.y != previous_mouse_pos_.y) {
945 // The mouse cursor is moved, so switch to the mouse-mode.
946 for(int i = 0 ; i < menus_.size() ; i ++) {
947 auto& menu = menus_[i] ;
948 auto detected_hwnd = WindowFromPoint(pos) ;
949 if(!detected_hwnd) {
950 return false ;
951 }
952 // Checks whether the mouse cursor is over the menu or not.
953 if(detected_hwnd == menu.window_handle()) {
954 // Start selection by key from the currently selected menu.
955 select_index_ = i ;
956 break ;
957 }
958 }
959 previous_mouse_pos_ = pos ;
960 }
961
962 if(select_index_ < 0) {
963 return true ;
964 }
965
966 // Update the color of only changed menu.
967 for(int i = 0 ; i < menus_.size() ; i ++) {
968 if(i == select_index_) {
969 if(!status_if_focus[i]) {
970 // OFF -> ON
971 if(!change_menu_back_color(menus_[i], border_color_)) {
972 fail() ;
973 return false ;
974 }
975 }
976 status_if_focus[i] = true ;
977 }
978 else {
979 if(status_if_focus[i]) {
980 // ON -> OFF
981 if(!change_menu_back_color(menus_[i], back_color_)) {
982 fail() ;
983 return false ;
984 }
985 }
986 status_if_focus[i] = false ;
987 }
988 }
989
990 return true ;
991 }
992
999 std::chrono::milliseconds sleep_time=std::chrono::milliseconds(1)) {
1000
1001 while(true) {
1002 if(status_ == TrayStatus::SHOULD_STOP) {
1003 status_ = TrayStatus::STOPPED ;
1004 break ;
1005 }
1006
1007 if(!update()) {
1008 return false ;
1009 }
1010
1011 Sleep(static_cast<int>(sleep_time.count())) ;
1012 }
1013 return true ;
1014 }
1015
1020 HWND window_handle() const noexcept {
1021 return hwnd_ ;
1022 }
1023
1029 // Initialize the color settings.
1030 if(back_color_ == CLR_INVALID) {
1031 // If the color is CLR_INVALID, it is determined from the theme.
1032 back_color_ = extract_taskbar_color() ;
1033 if(back_color_ == CLR_INVALID) {
1034 return false ;
1035 }
1036 if(!update_background_brush()) {
1037 return false ;
1038 }
1039 }
1040 if(text_color_ == CLR_INVALID) {
1041 // The text color is automatically determined from the background color.
1042 text_color_ = calculate_text_color_(back_color_) ;
1043 if(text_color_ == CLR_INVALID) {
1044 return false ;
1045 }
1046 }
1047 if(border_color_ == CLR_INVALID) {
1048 // The border color is automatically determined from the background color.
1049 border_color_ = calculate_faded_color_(back_color_, color_decay_) ;
1050 if(border_color_ == CLR_INVALID) {
1051 return false ;
1052 }
1053 }
1054
1055 LONG max_label_size = 0 ;
1056 for(auto& menu : menus_) {
1057 SIZE size ;
1058 if(!menu.calculate_required_dims(font_, size)) {
1059 return false ;
1060 }
1061 if(max_label_size < size.cx) {
1062 max_label_size = size.cx ;
1063 }
1064 }
1065
1066 // Update the sizes
1067 auto menu_width = max_label_size + 2 * menu_x_pad_ ;
1068 auto menu_height = menu_font_size_ + 2 * menu_y_pad_ ;
1069 auto popup_width = 2 * menu_x_margin_ + menu_width ;
1070 auto popup_height = static_cast<LONG>(
1071 menus_.size() * (menu_y_margin_ + menu_height) + menu_y_margin_) ;
1072
1073 POINT cursor_pos ;
1074 if(!GetCursorPos(&cursor_pos)) {
1075 return false ;
1076 }
1077
1078 RECT work_rect ;
1079 if(!SystemParametersInfo(
1080 SPI_GETWORKAREA, 0, reinterpret_cast<PVOID>(&work_rect), 0)) {
1081 return false ;
1082 }
1083
1084 auto screen_width = GetSystemMetrics(SM_CXSCREEN) ;
1085 auto screen_height = GetSystemMetrics(SM_CYSCREEN) ;
1086
1087 auto work_width = work_rect.right - work_rect.left ;
1088 auto work_height = work_rect.bottom - work_rect.top ;
1089
1090 auto taskbar_width = screen_width - work_width ;
1091 auto taskbar_height = screen_height - work_height ;
1092
1093 auto pos = cursor_pos ;
1094 if(taskbar_width == 0) { // horizontal taskbar
1095 if(cursor_pos.y <= taskbar_height) {
1096 //top
1097 pos.y = taskbar_height ;
1098 }
1099 else {
1100 //bottom
1101 // add 20% offset
1102 pos.y = screen_height - (popup_height + 12 * taskbar_height / 10) ;
1103 }
1104 pos.x = cursor_pos.x - popup_width / 2 ;
1105 }
1106 else { // vertical taskbar
1107 if(pos.x <= taskbar_width) {
1108 //left
1109 pos.x = taskbar_width ;
1110 }
1111 else {
1112 //right
1113 // add 20% offset
1114 pos.x = popup_width + 12 * taskbar_width / 10 ;
1115 }
1116
1117 pos.y = cursor_pos.y - popup_height / 2 ;
1118 }
1119
1120 if(!SetWindowPos(
1121 hwnd_, HWND_TOP,
1122 pos.x, pos.y, popup_width, popup_height,
1123 SWP_SHOWWINDOW)) {
1124 return false ;
1125 }
1126
1127 for(std::size_t i = 0 ; i < menus_.size() ; i ++) {
1128 auto& menu = menus_[i] ;
1129 auto y = \
1130 menu_y_margin_
1131 + static_cast<LONG>(i) * (menu_height + menu_y_margin_) ;
1132 if(!SetWindowPos(
1133 menu.window_handle(), HWND_TOP,
1134 menu_x_margin_, y,
1135 menu_width, menu_height,
1136 SWP_SHOWWINDOW)) {
1137 return false ;
1138 }
1139
1140 if(!menu.set_color(text_color_, back_color_, border_color_)) {
1141 return false ;
1142 }
1143 }
1144 std::fill(status_if_focus.begin(), status_if_focus.end(), false) ;
1145
1146 if(!SetForegroundWindow(hwnd_)) {
1147 return false ;
1148 }
1149
1150 visible_ = true ;
1151
1152 return true ;
1153 }
1154
1160 ShowWindow(hwnd_, SW_HIDE) ;
1161 visible_ = false ;
1162 select_index_ = -1 ;
1163 std::fill(status_if_focus.begin(), status_if_focus.end(), false) ;
1164 return true ;
1165 }
1166
1174 bool show_balloon_tip(const std::string& title, const std::string& message) {
1175 NOTIFYICONDATAW notify_data = {} ;
1176 notify_data.cbSize = sizeof(notify_data) ;
1177 notify_data.hWnd = hwnd_ ;
1178 notify_data.uFlags = NIF_INFO ;
1179
1180 std::wstring title_wide ;
1181 if(!util::string2wstring(title, title_wide)) {
1182 return false ;
1183 }
1184
1185 std::wstring message_wide ;
1186 if(!util::string2wstring(message, message_wide)) {
1187 return false ;
1188 }
1189
1190 auto title_len = title_wide.length() > 47 ? 47 : title_wide.length() ;
1191 std::wmemcpy(
1192 notify_data.szInfoTitle, title_wide.c_str(), title_len) ;
1193 notify_data.szInfoTitle[title_len] = L'\0' ;
1194
1195 auto message_len = message_wide.length() > 199 ? 199 : message_wide.length() ;
1196 std::wmemcpy(
1197 notify_data.szInfo, message_wide.c_str(), message_len) ;
1198 notify_data.szInfo[message_len] = L'\0' ;
1199
1200 if(!Shell_NotifyIconW(NIM_MODIFY, &notify_data)) {
1201 return false ;
1202 }
1203 return true ;
1204 }
1205
1210 TrayStatus status() const noexcept {
1211 return status_ ;
1212 }
1213
1217 void stop() noexcept {
1218 status_ = TrayStatus::SHOULD_STOP ;
1219 }
1220
1225 std::vector<FluentMenu>::iterator begin() noexcept {
1226 return menus_.begin() ;
1227 }
1228
1233 std::vector<FluentMenu>::iterator end() noexcept {
1234 return menus_.end() ;
1235 }
1236
1241 std::vector<FluentMenu>::const_iterator cbegin() const noexcept {
1242 return menus_.cbegin() ;
1243 }
1244
1249 std::vector<FluentMenu>::const_iterator cend() const noexcept {
1250 return menus_.cend() ;
1251 }
1252
1258 return menus_.front() ;
1259 }
1260
1265 const FluentMenu& front() const {
1266 return menus_.front() ;
1267 }
1268
1274 return menus_.back() ;
1275 }
1276
1281 const FluentMenu& back() const {
1282 return menus_.back() ;
1283 }
1284
1289 std::size_t count_menus() const noexcept {
1290 return menus_.size() ;
1291 }
1292
1302 LONG font_size=0,
1303 LONG font_weight=0,
1304 const std::string& font_name="") {
1305 NONCLIENTMETRICSW metrics ;
1306 metrics.cbSize = sizeof(metrics) ;
1307
1308 if(!SystemParametersInfoW(
1309 SPI_GETNONCLIENTMETRICS,
1310 metrics.cbSize, &metrics, 0)) {
1311 return false ;
1312 }
1313
1314 auto& logfont = metrics.lfCaptionFont ;
1315 if(font_size != 0) {
1316 logfont.lfHeight = font_size ;
1317 }
1318 else {
1319 logfont.lfHeight = 20 ;
1320 }
1321 if(font_weight != 0) {
1322 logfont.lfWeight = font_weight ;
1323 }
1324 else {
1325 logfont.lfWeight = FW_MEDIUM ;
1326 }
1327
1328 if(!font_name.empty()) {
1329 std::wstring font_name_wide ;
1330 if(!util::string2wstring(font_name, font_name_wide)) {
1331 return false ;
1332 }
1333 auto& dst = logfont.lfFaceName ;
1334
1335 if(font_name_wide.size() < LF_FACESIZE) {
1336 std::wmemcpy(
1337 dst, font_name_wide.c_str(), font_name_wide.length()) ;
1338 dst[font_name_wide.size()] = L'\0' ;
1339 }
1340 else {
1341 std::wmemcpy(
1342 dst, font_name_wide.c_str(), LF_FACESIZE - 1) ;
1343 dst[LF_FACESIZE - 1] = L'\0' ;
1344 }
1345 }
1346
1347 auto font = CreateFontIndirectW(&logfont) ;
1348 if(!font) {
1349 return false ;
1350 }
1351 font_ = font ;
1352 menu_font_size_ = std::abs(logfont.lfHeight) ;
1353
1354 return true;
1355 }
1356
1365 COLORREF text_color=CLR_INVALID,
1366 COLORREF back_color=CLR_INVALID,
1367 COLORREF border_color=CLR_INVALID) {
1368 if(back_color != CLR_INVALID) {
1369 back_color_ = back_color ;
1370 if(!update_background_brush()) {
1371 return false ;
1372 }
1373 }
1374 if(text_color != CLR_INVALID) {
1375 text_color_ = text_color ;
1376 }
1377 if(border_color != CLR_INVALID) {
1378 border_color_ = border_color ;
1379 }
1380 return true ;
1381 }
1382
1387 bool change_icon(const std::string& icon_path) {
1388 if(icon_data_.cbSize > 0) {
1389 if(!Shell_NotifyIconW(NIM_DELETE, &icon_data_)) {
1390 return false ;
1391 }
1392 }
1393
1394 ZeroMemory(&icon_data_, sizeof(icon_data_)) ;
1395
1396 if(icon_path.empty()) {
1397 icon_data_.cbSize = 0 ;
1398 return true ;
1399 }
1400
1401 std::wstring icon_path_wide ;
1402 if(!util::string2wstring(icon_path, icon_path_wide)) {
1403 return false ;
1404 }
1405
1406 if(!util::exists(icon_path_wide)) {
1407 return false ;
1408 }
1409
1410 icon_data_.cbSize = sizeof(icon_data_) ;
1411 icon_data_.hWnd = hwnd_ ;
1412 icon_data_.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP ;
1413 icon_data_.uCallbackMessage = message_id_ ;
1414 icon_data_.hIcon = static_cast<HICON>(
1415 LoadImageW(
1416 NULL, icon_path_wide.c_str(),
1417 IMAGE_ICON, 0, 0, LR_LOADFROMFILE)) ;
1418 wcscpy_s(icon_data_.szTip, app_name_.c_str()) ;
1419 icon_data_.dwState = NIS_SHAREDICON ;
1420 icon_data_.dwStateMask = NIS_SHAREDICON ;
1421
1422 if(!Shell_NotifyIconW(NIM_ADD, &icon_data_)) {
1423 return false ;
1424 }
1426
1427 return true ;
1428 }
1429
1430 private:
1431 static LRESULT CALLBACK callback(
1432 HWND hwnd,
1433 UINT msg,
1434 WPARAM wparam,
1435 LPARAM lparam) {
1436 auto get_instance = [hwnd]() -> FluentTray* {
1437 auto upper_addr = GetWindowLongW(hwnd, 0) ;
1438 if(!upper_addr) {
1439 return nullptr ;
1440 }
1441
1442 auto lower_addr = GetWindowLongW(hwnd, sizeof(LONG)) ;
1443 if(!lower_addr) {
1444 return nullptr ;
1445 }
1446
1447 FluentTray* self ;
1448 util::concatenate_bits(upper_addr, lower_addr, self) ;
1449 return self ;
1450 } ;
1451
1452 if(msg == WM_DESTROY || msg == WM_QUIT || msg == WM_CLOSE) {
1453 if(auto self = get_instance()) {
1454 self->stop() ;
1455 return 0 ;
1456 }
1457 }
1458 else if(msg == WM_ACTIVATE && wparam == WA_INACTIVE) {
1459 if(auto self = get_instance()) {
1460 if(!self->hide_menu_window()) {
1461 self->fail() ;
1462 }
1463 return 0 ;
1464 }
1465 }
1466 else if(msg == WM_DRAWITEM) {
1467 if(auto self = get_instance()) {
1468 auto item = reinterpret_cast<LPDRAWITEMSTRUCT>(lparam) ;
1469 auto menu_idx = self->get_menu_index_from_window(item->hwndItem) ;
1470 if(menu_idx < 0) {
1471 return FALSE ;
1472 }
1473 auto& menu = self->menus_[menu_idx] ;
1474 if(!menu.draw_menu(item, self->font_)) {
1475 self->fail() ;
1476 return FALSE ;
1477 }
1478 return TRUE ;
1479 }
1480 }
1481 else if(msg == WM_CTLCOLORBTN) {
1482 if(auto self = get_instance()) {
1483 auto menu_idx = self->get_menu_index_from_window(reinterpret_cast<HWND>(lparam)) ;
1484 if(menu_idx < 0) {
1485 return DefWindowProc(hwnd, msg, wparam, lparam) ;
1486 }
1487 auto& menu = self->menus_[menu_idx] ;
1488 return reinterpret_cast<LRESULT>(menu.background_brush()) ;
1489 }
1490 }
1491 else if(msg == WM_COMMAND) {
1492 if(auto self = get_instance()) {
1493 auto menu_idx = self->get_menu_index_from_id(LOWORD(wparam)) ;
1494 if(menu_idx < 0) {
1495 return FALSE ;
1496 }
1497 auto& menu = self->menus_[menu_idx] ;
1498 if(!menu.process_click_event()) {
1499 self->stop() ;
1500 return FALSE ;
1501 }
1502 if(!self->hide_menu_window()) {
1503 return FALSE ;
1504 }
1505 return TRUE ;
1506 }
1507 }
1508 else if(msg == WM_KEYDOWN) {
1509 if(auto self = get_instance()) {
1510 if(wparam == VK_DOWN) {
1511 if(self->select_index_ < 0) {
1512 // Initialize the position of bounding box cursor
1513 self->select_index_ = 0 ;
1514 }
1515 else {
1516 self->select_index_ = (self->select_index_ + 1) % self->menus_.size() ;
1517 }
1518 return TRUE;
1519 }
1520 else if(wparam == VK_UP) {
1521 if(self->select_index_ < 0) {
1522 // Initialize the position of bounding box cursor
1523 self->select_index_ = static_cast<int>(self->menus_.size() - 1) ;
1524 }
1525 else {
1526 auto mod = static_cast<int>(self->menus_.size()) ;
1527 self->select_index_ = ((self->select_index_ - 1) % mod + mod) % mod ; // to be positive
1528 }
1529 return TRUE;
1530 }
1531 else if(wparam == VK_ESCAPE) {
1532 if(!self->hide_menu_window()) {
1533 return FALSE ;
1534 }
1535 return TRUE;
1536 }
1537 else if(wparam == VK_SPACE || wparam == VK_RETURN) {
1538 if(self->select_index_ >= 0) {
1539 auto& menu = self->menus_[self->select_index_] ;
1540 if(!menu.process_click_event()) {
1541 self->stop() ;
1542 return FALSE ;
1543 }
1544 if(!self->hide_menu_window()) {
1545 return FALSE ;
1546 }
1547 }
1548 return TRUE;
1549 }
1550 }
1551 }
1552 else if(msg == message_id_) { //On NotifyIcon
1553 if(auto self = get_instance()) {
1554 if(lparam == WM_LBUTTONUP || lparam == WM_RBUTTONUP) {
1555 self->show_menu_window() ;
1556 return 0 ;
1557 }
1558 }
1559 }
1560
1561 return DefWindowProc(hwnd, msg, wparam, lparam) ;
1562 }
1563
1564 int get_menu_index_from_window(HWND hwnd) {
1565 int i = 0 ;
1566 for(auto& m : menus_) {
1567 if(m.window_handle() == hwnd) {
1568 return i ;
1569 }
1570 i ++ ;
1571 }
1572 return -1 ;
1573 }
1574
1575 int get_menu_index_from_id(WORD id) {
1576 int i = 0 ;
1577 for(auto& m : menus_) {
1578 if(m.id() == static_cast<std::size_t>(id)) {
1579 return i ;
1580 }
1581 i ++ ;
1582 }
1583 return -1 ;
1584 }
1585
1586 void get_message(MSG& message) {
1587 if(PeekMessage(&message, hwnd_, 0, 0, PM_REMOVE)) {
1588 DispatchMessage(&message) ;
1589 }
1590 }
1591
1592 void fail() noexcept {
1593 status_ = TrayStatus::FAILED ;
1594 }
1595
1596 bool change_menu_back_color(FluentMenu& menu, COLORREF new_color) {
1597 if(!menu.set_color(
1598 text_color_, new_color, border_color_)) {
1599 return false ;
1600 }
1601 // Redraw
1602 if(!InvalidateRect(menu.window_handle(), NULL, TRUE)) {
1603 return false ;
1604 }
1605 return true ;
1606 }
1607
1608 COLORREF extract_taskbar_color() const {
1609 // Get Taskbar color
1610 APPBARDATA abd ;
1611 abd.cbSize = sizeof(abd) ;
1612 if(!SHAppBarMessage(ABM_GETTASKBARPOS, &abd)) {
1613 return CLR_INVALID ;
1614 }
1615
1616 COLORREF color = CLR_INVALID ;
1617 if(auto dc = GetDC(NULL)) {
1618 // Get Taskbar color
1619 color = GetPixel(
1620 dc,
1621 abd.rc.left + autocolorpick_offset_,
1622 abd.rc.top + autocolorpick_offset_) ;
1623 if(color == CLR_INVALID) {
1624 // if failed, use COLOR_WINDOW color.
1625 color = GetSysColor(COLOR_WINDOW) ;
1626 }
1627 if(!ReleaseDC(NULL, dc)) {
1628 return CLR_INVALID ;
1629 }
1630 }
1631 return color ;
1632 }
1633
1634 static COLORREF calculate_faded_color_(
1635 COLORREF back_color,
1636 unsigned char color_decay=10) {
1637 auto back_gray_color = util::rgb2gray(back_color) ;
1638
1639 unsigned char ash_value = back_gray_color ;
1640 if(back_gray_color < 128) {
1641 ash_value = static_cast<decltype(ash_value)>(
1642 (std::min)(ash_value + color_decay, 255)) ;
1643 }
1644 else {
1645 ash_value = static_cast<decltype(ash_value)>(
1646 (std::max)(ash_value - color_decay, 0)) ;
1647 }
1648 return RGB(ash_value, ash_value, ash_value) ;
1649 }
1650
1651 static COLORREF calculate_text_color_(COLORREF back_color) {
1652 auto back_gray_color = util::rgb2gray(back_color) ;
1653
1654 auto text_color = GetSysColor(COLOR_WINDOWTEXT) ;
1655 if(back_gray_color < 128) {
1656 // if dark background, use light text color.
1657 text_color = 0x00FFFFFF & ~text_color ;
1658 }
1659 return text_color ;
1660 }
1661
1662 bool update_background_brush() {
1663 if(back_brush_) {
1664 // Release old handle.
1665 if(!DeleteObject(back_brush_)) {
1666 return false ;
1667 }
1668 }
1669 back_brush_ = CreateSolidBrush(back_color_) ;
1670 if(back_brush_ == NULL) {
1671 return false ;
1672 }
1673
1674 if(!SetClassLongPtr(
1675 hwnd_, GCLP_HBRBACKGROUND,
1676 reinterpret_cast<LONG_PTR>(back_brush_))) {
1677 return false ;
1678 }
1679
1680 return true ;
1681 }
1682 } ;
1683
1684 unsigned int FluentTray::message_id_ ;
1685}
1686
1687#endif
Class with information on each menu.
Definition fluent_tray.hpp:258
bool set_color(const COLORREF &text_color=CLR_INVALID, const COLORREF &back_color=CLR_INVALID, const COLORREF &border_color=CLR_INVALID) noexcept
Set the menu color.
Definition fluent_tray.hpp:492
FluentMenu(bool toggleable=false, const std::function< bool(void)> &callback=[] {return true ;}, const std::function< bool(void)> &unchecked_callback=[] {return true ;})
Create menu object.
Definition fluent_tray.hpp:288
FluentMenu & operator=(FluentMenu &&)=default
bool draw_menu(LPDRAWITEMSTRUCT info, HFONT font) const
Draws a menu using drawing information and the specified font.
Definition fluent_tray.hpp:528
FluentMenu(FluentMenu &&)=default
void show_separator_line() noexcept
Show a separator line under the menu.
Definition fluent_tray.hpp:473
HMENU menu_handle() const noexcept
Refer to the menu handle.
Definition fluent_tray.hpp:450
FluentMenu & operator=(const FluentMenu &)=default
std::size_t id() const noexcept
Refer to the menu identifier.
Definition fluent_tray.hpp:458
bool is_checked() const noexcept
Refer to the check status of the menu.
Definition fluent_tray.hpp:423
bool is_toggleable() const noexcept
Check if the menu is toggleable.
Definition fluent_tray.hpp:434
~FluentMenu() noexcept
Definition fluent_tray.hpp:314
bool create_menu(HINSTANCE hinstance, HWND parent_hwnd, std::size_t id, const std::string &label_text="", const std::string &icon_path="", const std::string &checkmark="✓")
Creates a menu window.
Definition fluent_tray.hpp:330
void uncheck() noexcept
Unchecks the menu if it is toggleable.
Definition fluent_tray.hpp:413
bool calculate_required_dims(HFONT font, SIZE &size) const
Calculates the size of the bounding box surrounding the menu based on the font information and the le...
Definition fluent_tray.hpp:614
HWND window_handle() const noexcept
Refer to the menu window handle.
Definition fluent_tray.hpp:442
void hide_separator_line() noexcept
Hide a separator line under the menu.
Definition fluent_tray.hpp:480
bool process_click_event()
Execute the process when clicked on the menu.
Definition fluent_tray.hpp:389
HBRUSH background_brush() const noexcept
Refer to the brush for drawing the background.
Definition fluent_tray.hpp:605
bool get_label(std::string &str) const
Get menu label as UTF-8 string.
Definition fluent_tray.hpp:466
void check() noexcept
Checks the menu if it is toggleable.
Definition fluent_tray.hpp:403
FluentMenu(const FluentMenu &)=default
Class with information on the entire tray.
Definition fluent_tray.hpp:664
bool show_balloon_tip(const std::string &title, const std::string &message)
Shows a balloon tip that is placed in the notification area.
Definition fluent_tray.hpp:1174
virtual ~FluentTray() noexcept
Definition fluent_tray.hpp:754
bool set_color(COLORREF text_color=CLR_INVALID, COLORREF back_color=CLR_INVALID, COLORREF border_color=CLR_INVALID)
Set colors to draw menus.
Definition fluent_tray.hpp:1364
std::vector< FluentMenu >::iterator end() noexcept
Returns an iterator to the end of menus.
Definition fluent_tray.hpp:1233
FluentTray & operator=(const FluentTray &)=delete
bool show_menu_window()
Show the menu window above the tray icon.
Definition fluent_tray.hpp:1028
void stop() noexcept
Exit the tray successfully.
Definition fluent_tray.hpp:1217
FluentTray & operator=(FluentTray &&)=default
bool add_menu(const std::string &label_text="", const std::string &icon_path="", bool toggleable=false, const std::string &checkmark="✓", const std::function< bool(void)> &callback=[] {return true ;}, const std::function< bool(void)> &unchecked_callback=[] {return true ;})
Add a menu in order from the top.
Definition fluent_tray.hpp:891
bool set_font(LONG font_size=0, LONG font_weight=0, const std::string &font_name="")
Set font information to draw menus.
Definition fluent_tray.hpp:1301
TrayStatus status() const noexcept
Get the current status of tray.
Definition fluent_tray.hpp:1210
const FluentMenu & back() const
Returns the const reference to the last of menus.
Definition fluent_tray.hpp:1281
bool change_icon(const std::string &icon_path)
Load the image file and change the icon.
Definition fluent_tray.hpp:1387
std::size_t count_menus() const noexcept
Returns the number of menus.
Definition fluent_tray.hpp:1289
bool update_with_loop(std::chrono::milliseconds sleep_time=std::chrono::milliseconds(1))
Create a message loop to update the tray.
Definition fluent_tray.hpp:998
std::vector< FluentMenu >::const_iterator cbegin() const noexcept
Returns a constant iterator to the beginning of menus.
Definition fluent_tray.hpp:1241
std::vector< FluentMenu >::const_iterator cend() const noexcept
Returns a constant iterator to the end of menus.
Definition fluent_tray.hpp:1249
const FluentMenu & front() const
Returns the const reference to the beginning of menus.
Definition fluent_tray.hpp:1265
void add_separator()
Add a separator line under the last menu item added.
Definition fluent_tray.hpp:914
bool hide_menu_window()
Hide the menu window above the tray icon.
Definition fluent_tray.hpp:1159
FluentTray(const FluentTray &)=delete
FluentMenu & front()
Returns the reference to the beginning of menus.
Definition fluent_tray.hpp:1257
FluentTray(FluentTray &&)=default
HWND window_handle() const noexcept
Refer to the handle of menu window.
Definition fluent_tray.hpp:1020
std::vector< FluentMenu >::iterator begin() noexcept
Returns an iterator to the beginning of menus.
Definition fluent_tray.hpp:1225
bool update()
Get window message and update tray.
Definition fluent_tray.hpp:924
bool create_tray(const std::string &app_name, const std::string &icon_path="", unsigned char opacity=255, bool round_corner=true)
Initialize tray and create icon on tray.
Definition fluent_tray.hpp:771
FluentTray(LONG menu_x_margin=5, LONG menu_y_margin=5, LONG menu_x_pad=10, LONG menu_y_pad=5, unsigned char autofadedborder_from_backcolor=10, int autocolorpick_offset=5, int message_id_offset=25)
Create tray object.
Definition fluent_tray.hpp:711
FluentMenu & back()
Returns the reference to the last of menus.
Definition fluent_tray.hpp:1273
bool exists(const std::wstring &path)
Checks if the file exists.
Definition fluent_tray.hpp:231
constexpr std::size_t bit2mask(std::size_t bits) noexcept
Generates a mask with the specified number of lower bits set to 1.
Definition fluent_tray.hpp:168
bool wstring2string(const std::wstring &wstr, std::string &str)
Converts a wide string to a UTF-8 encoded string.
Definition fluent_tray.hpp:133
void concatenate_bits(InType upper, InType lower, OutType &out) noexcept
Generates a variable that combines the bits of two variables.
Definition fluent_tray.hpp:206
bool string2wstring(const std::string &str, std::wstring &wstr)
Converts a UTF-8 encoded string to a wide string.
Definition fluent_tray.hpp:98
void split_bits(InType input, OutType &upper, OutType &lower) noexcept
Divides the input value into upper and lower bits.
Definition fluent_tray.hpp:190
constexpr int type2bit() noexcept
Calculate the number of bits of type.
Definition fluent_tray.hpp:177
unsigned char rgb2gray(const COLORREF &rgb)
Calculate grayscale value from RGB.
Definition fluent_tray.hpp:219
Base namespace.
TrayStatus
Current tray status.
Definition fluent_tray.hpp:241
@ STOPPED
The tray is stopped successfully.
@ RUNNING
The tray is working properly.
@ SHOULD_STOP
The tray is trying to exit successfully.
@ FAILED
The tray has errors.