33#ifndef _FLUENT_TRAY_HPP
34#define _FLUENT_TRAY_HPP
38#if defined(_MSC_VER) && _MSC_VER >= 1500
39#pragma warning(disable : 4005)
44#if defined(_MSC_VER) && _MSC_VER >= 1500
45#pragma warning(default : 4005)
50#ifndef DOXYGEN_SHOULD_SKIP_THIS
52#ifndef DWMWA_COLOR_DEFAULT
53#define DWMWA_WINDOW_CORNER_PREFERENCE static_cast<DWMWINDOWATTRIBUTE>(33)
56 DWMWCP_DONOTROUND = 1,
59} DWM_WINDOW_CORNER_PREFERENCE;
68#pragma comment(lib, "Dwmapi")
104 auto needed_size = MultiByteToWideChar(
106 str.c_str(),
static_cast<int>(str.length()),
108#ifndef _FLUENT_TRAY_IGNORE_IN_TEST
109 if(needed_size <= 0) {
114 wstr.resize(needed_size) ;
115 if(MultiByteToWideChar(
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
139 auto needed_size = WideCharToMultiByte(
141 wstr.c_str(),
static_cast<int>(wstr.length()),
144#ifndef _FLUENT_TRAY_IGNORE_IN_TEST
145 if(needed_size <= 0) {
150 str.resize(needed_size) ;
151 if(WideCharToMultiByte(
153 wstr.c_str(),
static_cast<int>(wstr.length()),
154 &str[0],
static_cast<int>(str.size()),
156#ifndef _FLUENT_TRAY_IGNORE_IN_TEST
168 inline constexpr std::size_t
bit2mask(std::size_t bits)
noexcept {
169 return (
static_cast<std::size_t
>(1) << bits) - 1 ;
176 template <
typename Type>
178 return static_cast<int>(
sizeof(Type) * CHAR_BIT) ;
189 template <
typename InType,
typename OutType>
190 inline void split_bits(InType input, OutType& upper, OutType& lower)
noexcept {
205 template <
typename InType,
typename OutType>
223 return static_cast<unsigned char>(0.2126 *
r + 0.7152 *
g + 0.0722 *
b) ;
260 std::wstring label_ ;
265 std::wstring checkmark_ ;
272 COLORREF text_color_ ;
273 COLORREF back_color_ ;
274 COLORREF border_color_ ;
277 std::function<bool(
void)> callback_ ;
278 std::function<bool(
void)> unchecked_callback_ ;
289 bool toggleable=
false,
290 const std::function<
bool(
void)>& callback=[] {
return true ;},
291 const std::function<bool(
void)>& unchecked_callback=[] {
return true ;})
294 toggleable_(toggleable),
300 text_color_(RGB(0, 0, 0)),
301 back_color_(RGB(255, 255, 255)),
302 border_color_(RGB(128, 128, 128)),
305 unchecked_callback_(unchecked_callback)
316 DeleteObject(back_brush_) ;
334 const std::string& label_text=
"",
335 const std::string& icon_path=
"",
336 const std::string& checkmark=
"✓") {
346 auto style = WS_CHILD | WS_VISIBLE | BS_FLAT | BS_LEFT | BS_OWNERDRAW ;
348 hmenu_ =
reinterpret_cast<HMENU
>(
id) ;
349 hwnd_ = CreateWindowW(
350 TEXT(
"BUTTON"), label_.c_str(), style,
360 hwnd_, WM_CHANGEUISTATE,
361 WPARAM(MAKELONG(UIS_SET, UISF_HIDEFOCUS)), 0) ;
363 if(!icon_path.empty()) {
364 std::wstring icon_path_wide ;
373 hicon_ =
static_cast<HICON
>(LoadImageW(
374 NULL, icon_path_wide.c_str(),
375 IMAGE_ICON, 0, 0, LR_LOADFROMFILE)) ;
391 checked_ = !checked_ ;
393 return unchecked_callback_() ;
458 std::size_t
id() const noexcept {
459 return reinterpret_cast<std::size_t
>(hmenu_) ;
481 under_line_ = false ;
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 ;
499 if(back_color != CLR_INVALID) {
500 back_color_ = back_color ;
502 if(border_color != CLR_INVALID) {
503 border_color_ = border_color ;
509 if(!DeleteObject(back_brush_)) {
513 back_brush_ = CreateSolidBrush(back_color_) ;
514 if(back_brush_ == NULL) {
528 bool draw_menu(LPDRAWITEMSTRUCT info, HFONT font)
const {
529 if(SetTextColor(info->hDC, text_color_) == CLR_INVALID) {
533 if(SetBkColor(info->hDC, back_color_) == CLR_INVALID) {
538 if(!SelectObject(info->hDC, font)) {
543 LONG checkmark_size, label_height, icon_size, margin ;
544 if(!calculate_layouts(
546 checkmark_size, label_height, icon_size, margin)) {
550 auto& rect = info->rcItem ;
552 auto y_center = rect.top + (rect.bottom - rect.top) / 2 ;
553 auto x = rect.left + margin ;
555 if(toggleable_ && checked_) {
557 info->hDC, x, y_center - label_height / 2, checkmark_.c_str(),
558 static_cast<int>(checkmark_.length()))) {
562 x += checkmark_size + margin ;
566 info->hDC, x, y_center - SM_CYICON / 2, hicon_,
567 icon_size, icon_size, 0, NULL, DI_NORMAL)) {
571 x += icon_size + margin ;
574 info->hDC, x, y_center - label_height / 2, label_.c_str(),
575 static_cast<int>(label_.length()))) {
580 auto original_obj = SelectObject(info->hDC, GetStockObject(DC_PEN)) ;
581 if(SetDCPenColor(info->hDC, border_color_) == CLR_INVALID) {
585 auto lx = info->rcItem.left ;
586 auto ly = info->rcItem.bottom - 1 ;
587 auto rx = info->rcItem.right ;
589 if(!Rectangle(info->hDC, lx, ly, rx, ry)) {
593 if(!SelectObject(info->hDC, original_obj)) {
615 auto hdc = GetDC(hwnd_) ;
617 if(!SelectObject(hdc, font)) {
622 if(!GetTextExtentPoint32W(
624 static_cast<int>(label_.length()), &size)) {
628 LONG checkmark_size, label_height, icon_size, margin ;
629 if(!calculate_layouts(
630 hdc, checkmark_size, label_height, icon_size, margin)) {
634 size.cx += margin + checkmark_size + margin + icon_size + margin ;
641 bool calculate_layouts(
643 LONG& checkmark_size,
646 LONG& margin)
const {
648 if(!GetTextExtentPoint32W(hdc, L
" ", 1, &size)) {
651 checkmark_size = size.cy ;
652 margin = size.cy / 2 ;
653 label_height = size.cy ;
654 icon_size = 4 * label_height / 5 ;
666 std::wstring app_name_ ;
668 HINSTANCE hinstance_ ;
671 NOTIFYICONDATAW icon_data_ ;
675 std::vector<FluentMenu> menus_ ;
676 std::vector<bool> status_if_focus ;
677 std::size_t next_menu_id_ ;
680 POINT previous_mouse_pos_ ;
682 LONG menu_x_margin_ ;
683 LONG menu_y_margin_ ;
688 COLORREF text_color_ ;
689 COLORREF back_color_ ;
690 COLORREF border_color_ ;
691 unsigned char color_decay_ ;
693 int autocolorpick_offset_ ;
695 LONG menu_font_size_ ;
698 static unsigned int message_id_ ;
712 LONG menu_x_margin=5,
713 LONG menu_y_margin=5,
716 unsigned char autofadedborder_from_backcolor=10,
717 int autocolorpick_offset=5,
718 int message_id_offset=25)
720 hinstance_(GetModuleHandle(NULL)),
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),
739 autocolorpick_offset_(autocolorpick_offset),
743 message_id_ = WM_APP + message_id_offset ;
756 DeleteObject(font_) ;
758 if(back_brush_ != NULL) {
759 DeleteObject(back_brush_) ;
772 const std::string& app_name,
773 const std::string& icon_path=
"",
774 unsigned char opacity=255,
775 bool round_corner=
true) {
781 winc.style = CS_HREDRAW | CS_VREDRAW ;
782 winc.lpfnWndProc = &FluentTray::callback ;
783 winc.cbClsExtra = 0 ;
784 winc.cbWndExtra =
sizeof(LONG) * 2 ;
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() ;
792 if(!RegisterClassW(&winc)) {
796 hwnd_ = CreateWindowExW(
797 WS_EX_TOOLWINDOW | WS_EX_LAYERED,
811 LONG upper_addr, lower_addr ;
815 if(!SetWindowLongW(hwnd_, 0, upper_addr) && GetLastError() != 0) {
819 if(!SetWindowLongW(hwnd_,
sizeof(LONG), lower_addr) && GetLastError() != 0) {
823 if(!SetLayeredWindowAttributes(hwnd_, 0, opacity, LWA_ALPHA)) {
829#if defined(_MSC_VER) && _MSC_VER >= 1500
830 using RtlGetVersionType = NTSTATUS (WINAPI*)(PRTL_OSVERSIONINFOW) ;
831 const auto hmodule = LoadLibraryW(L
"ntdll.dll") ;
837 const auto RtlGetVersion =
reinterpret_cast<RtlGetVersionType
>(
838 reinterpret_cast<void*
>(GetProcAddress(hmodule,
"RtlGetVersion"))) ;
840 FreeLibrary(hmodule) ;
844 OSVERSIONINFOW vinfo ;
845 vinfo.dwOSVersionInfoSize =
sizeof(
decltype(vinfo)) ;
846 auto result = RtlGetVersion(&vinfo) ;
847 if(!FreeLibrary(hmodule)) {
851 if(result != STATUS_SUCCESS) {
856 if(vinfo.dwBuildNumber >= 22000) {
857 auto pref = DWMWCP_ROUND ;
858 if(DwmSetWindowAttribute(
860 DWMWA_WINDOW_CORNER_PREFERENCE,
861 &pref,
sizeof(pref)) != S_OK) {
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)) {
905 menus_.push_back(std::move(menu)) ;
906 status_if_focus.push_back(
false) ;
915 if(!menus_.empty()) {
916 menus_.back().show_separator_line() ;
932 if(GetForegroundWindow() != hwnd_ && visible_) {
940 if(!GetCursorPos(&pos)) {
944 if(pos.x != previous_mouse_pos_.x || pos.y != previous_mouse_pos_.y) {
946 for(
int i = 0 ; i < menus_.size() ; i ++) {
947 auto& menu = menus_[i] ;
948 auto detected_hwnd = WindowFromPoint(pos) ;
953 if(detected_hwnd == menu.window_handle()) {
959 previous_mouse_pos_ = pos ;
962 if(select_index_ < 0) {
967 for(
int i = 0 ; i < menus_.size() ; i ++) {
968 if(i == select_index_) {
969 if(!status_if_focus[i]) {
971 if(!change_menu_back_color(menus_[i], border_color_)) {
976 status_if_focus[i] = true ;
979 if(status_if_focus[i]) {
981 if(!change_menu_back_color(menus_[i], back_color_)) {
986 status_if_focus[i] = false ;
999 std::chrono::milliseconds sleep_time=std::chrono::milliseconds(1)) {
1011 Sleep(
static_cast<int>(sleep_time.count())) ;
1030 if(back_color_ == CLR_INVALID) {
1032 back_color_ = extract_taskbar_color() ;
1033 if(back_color_ == CLR_INVALID) {
1036 if(!update_background_brush()) {
1040 if(text_color_ == CLR_INVALID) {
1042 text_color_ = calculate_text_color_(back_color_) ;
1043 if(text_color_ == CLR_INVALID) {
1047 if(border_color_ == CLR_INVALID) {
1049 border_color_ = calculate_faded_color_(back_color_, color_decay_) ;
1050 if(border_color_ == CLR_INVALID) {
1055 LONG max_label_size = 0 ;
1056 for(
auto& menu : menus_) {
1058 if(!menu.calculate_required_dims(font_, size)) {
1061 if(max_label_size < size.cx) {
1062 max_label_size = size.cx ;
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_) ;
1074 if(!GetCursorPos(&cursor_pos)) {
1079 if(!SystemParametersInfo(
1080 SPI_GETWORKAREA, 0,
reinterpret_cast<PVOID
>(&work_rect), 0)) {
1084 auto screen_width = GetSystemMetrics(SM_CXSCREEN) ;
1085 auto screen_height = GetSystemMetrics(SM_CYSCREEN) ;
1087 auto work_width = work_rect.right - work_rect.left ;
1088 auto work_height = work_rect.bottom - work_rect.top ;
1090 auto taskbar_width = screen_width - work_width ;
1091 auto taskbar_height = screen_height - work_height ;
1093 auto pos = cursor_pos ;
1094 if(taskbar_width == 0) {
1095 if(cursor_pos.y <= taskbar_height) {
1097 pos.y = taskbar_height ;
1102 pos.y = screen_height - (popup_height + 12 * taskbar_height / 10) ;
1104 pos.x = cursor_pos.x - popup_width / 2 ;
1107 if(pos.x <= taskbar_width) {
1109 pos.x = taskbar_width ;
1114 pos.x = popup_width + 12 * taskbar_width / 10 ;
1117 pos.y = cursor_pos.y - popup_height / 2 ;
1122 pos.x, pos.y, popup_width, popup_height,
1127 for(std::size_t i = 0 ; i < menus_.size() ; i ++) {
1128 auto& menu = menus_[i] ;
1131 +
static_cast<LONG
>(i) * (menu_height + menu_y_margin_) ;
1133 menu.window_handle(), HWND_TOP,
1135 menu_width, menu_height,
1140 if(!menu.set_color(text_color_, back_color_, border_color_)) {
1144 std::fill(status_if_focus.begin(), status_if_focus.end(),
false) ;
1146 if(!SetForegroundWindow(hwnd_)) {
1160 ShowWindow(hwnd_, SW_HIDE) ;
1162 select_index_ = -1 ;
1163 std::fill(status_if_focus.begin(), status_if_focus.end(),
false) ;
1175 NOTIFYICONDATAW notify_data = {} ;
1176 notify_data.cbSize =
sizeof(notify_data) ;
1177 notify_data.hWnd = hwnd_ ;
1178 notify_data.uFlags = NIF_INFO ;
1180 std::wstring title_wide ;
1185 std::wstring message_wide ;
1190 auto title_len = title_wide.length() > 47 ? 47 : title_wide.length() ;
1192 notify_data.szInfoTitle, title_wide.c_str(), title_len) ;
1193 notify_data.szInfoTitle[title_len] = L
'\0' ;
1195 auto message_len = message_wide.length() > 199 ? 199 : message_wide.length() ;
1197 notify_data.szInfo, message_wide.c_str(), message_len) ;
1198 notify_data.szInfo[message_len] = L
'\0' ;
1200 if(!Shell_NotifyIconW(NIM_MODIFY, ¬ify_data)) {
1225 std::vector<FluentMenu>::iterator
begin() noexcept {
1226 return menus_.begin() ;
1233 std::vector<FluentMenu>::iterator
end() noexcept {
1234 return menus_.end() ;
1241 std::vector<FluentMenu>::const_iterator
cbegin() const noexcept {
1242 return menus_.cbegin() ;
1249 std::vector<FluentMenu>::const_iterator
cend() const noexcept {
1250 return menus_.cend() ;
1258 return menus_.front() ;
1266 return menus_.front() ;
1274 return menus_.back() ;
1282 return menus_.back() ;
1290 return menus_.size() ;
1304 const std::string& font_name=
"") {
1305 NONCLIENTMETRICSW metrics ;
1306 metrics.cbSize =
sizeof(metrics) ;
1308 if(!SystemParametersInfoW(
1309 SPI_GETNONCLIENTMETRICS,
1310 metrics.cbSize, &metrics, 0)) {
1314 auto& logfont = metrics.lfCaptionFont ;
1315 if(font_size != 0) {
1316 logfont.lfHeight = font_size ;
1319 logfont.lfHeight = 20 ;
1321 if(font_weight != 0) {
1322 logfont.lfWeight = font_weight ;
1325 logfont.lfWeight = FW_MEDIUM ;
1328 if(!font_name.empty()) {
1329 std::wstring font_name_wide ;
1333 auto& dst = logfont.lfFaceName ;
1335 if(font_name_wide.size() < LF_FACESIZE) {
1337 dst, font_name_wide.c_str(), font_name_wide.length()) ;
1338 dst[font_name_wide.size()] = L
'\0' ;
1342 dst, font_name_wide.c_str(), LF_FACESIZE - 1) ;
1343 dst[LF_FACESIZE - 1] = L
'\0' ;
1347 auto font = CreateFontIndirectW(&logfont) ;
1352 menu_font_size_ = std::abs(logfont.lfHeight) ;
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()) {
1374 if(text_color != CLR_INVALID) {
1375 text_color_ = text_color ;
1377 if(border_color != CLR_INVALID) {
1378 border_color_ = border_color ;
1388 if(icon_data_.cbSize > 0) {
1389 if(!Shell_NotifyIconW(NIM_DELETE, &icon_data_)) {
1394 ZeroMemory(&icon_data_,
sizeof(icon_data_)) ;
1396 if(icon_path.empty()) {
1397 icon_data_.cbSize = 0 ;
1401 std::wstring icon_path_wide ;
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
>(
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 ;
1422 if(!Shell_NotifyIconW(NIM_ADD, &icon_data_)) {
1431 static LRESULT CALLBACK callback(
1436 auto get_instance = [hwnd]() ->
FluentTray* {
1437 auto upper_addr = GetWindowLongW(hwnd, 0) ;
1442 auto lower_addr = GetWindowLongW(hwnd,
sizeof(LONG)) ;
1452 if(msg == WM_DESTROY || msg == WM_QUIT || msg == WM_CLOSE) {
1453 if(
auto self = get_instance()) {
1458 else if(msg == WM_ACTIVATE && wparam == WA_INACTIVE) {
1459 if(
auto self = get_instance()) {
1460 if(!self->hide_menu_window()) {
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) ;
1473 auto& menu = self->menus_[menu_idx] ;
1474 if(!menu.draw_menu(item, self->font_)) {
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)) ;
1485 return DefWindowProc(hwnd, msg, wparam, lparam) ;
1487 auto& menu = self->menus_[menu_idx] ;
1488 return reinterpret_cast<LRESULT
>(menu.background_brush()) ;
1491 else if(msg == WM_COMMAND) {
1492 if(
auto self = get_instance()) {
1493 auto menu_idx = self->get_menu_index_from_id(LOWORD(wparam)) ;
1497 auto& menu = self->menus_[menu_idx] ;
1498 if(!menu.process_click_event()) {
1502 if(!self->hide_menu_window()) {
1508 else if(msg == WM_KEYDOWN) {
1509 if(
auto self = get_instance()) {
1510 if(wparam == VK_DOWN) {
1511 if(self->select_index_ < 0) {
1513 self->select_index_ = 0 ;
1516 self->select_index_ = (self->select_index_ + 1) % self->menus_.size() ;
1520 else if(wparam == VK_UP) {
1521 if(self->select_index_ < 0) {
1523 self->select_index_ =
static_cast<int>(self->menus_.size() - 1) ;
1526 auto mod =
static_cast<int>(self->menus_.size()) ;
1527 self->select_index_ = ((self->select_index_ - 1) % mod + mod) % mod ;
1531 else if(wparam == VK_ESCAPE) {
1532 if(!self->hide_menu_window()) {
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()) {
1544 if(!self->hide_menu_window()) {
1552 else if(msg == message_id_) {
1553 if(
auto self = get_instance()) {
1554 if(lparam == WM_LBUTTONUP || lparam == WM_RBUTTONUP) {
1555 self->show_menu_window() ;
1561 return DefWindowProc(hwnd, msg, wparam, lparam) ;
1564 int get_menu_index_from_window(HWND hwnd) {
1566 for(
auto& m : menus_) {
1567 if(m.window_handle() == hwnd) {
1575 int get_menu_index_from_id(WORD
id) {
1577 for(
auto& m : menus_) {
1578 if(m.id() ==
static_cast<std::size_t
>(
id)) {
1586 void get_message(MSG& message) {
1587 if(PeekMessage(&message, hwnd_, 0, 0, PM_REMOVE)) {
1588 DispatchMessage(&message) ;
1592 void fail() noexcept {
1596 bool change_menu_back_color(FluentMenu& menu, COLORREF new_color) {
1598 text_color_, new_color, border_color_)) {
1602 if(!InvalidateRect(menu.window_handle(), NULL, TRUE)) {
1608 COLORREF extract_taskbar_color()
const {
1611 abd.cbSize =
sizeof(abd) ;
1612 if(!SHAppBarMessage(ABM_GETTASKBARPOS, &abd)) {
1613 return CLR_INVALID ;
1616 COLORREF color = CLR_INVALID ;
1617 if(
auto dc = GetDC(NULL)) {
1621 abd.rc.left + autocolorpick_offset_,
1622 abd.rc.top + autocolorpick_offset_) ;
1623 if(color == CLR_INVALID) {
1625 color = GetSysColor(COLOR_WINDOW) ;
1627 if(!ReleaseDC(NULL, dc)) {
1628 return CLR_INVALID ;
1634 static COLORREF calculate_faded_color_(
1635 COLORREF back_color,
1636 unsigned char color_decay=10) {
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)) ;
1645 ash_value =
static_cast<decltype(ash_value)
>(
1646 (std::max)(ash_value - color_decay, 0)) ;
1648 return RGB(ash_value, ash_value, ash_value) ;
1651 static COLORREF calculate_text_color_(COLORREF back_color) {
1654 auto text_color = GetSysColor(COLOR_WINDOWTEXT) ;
1655 if(back_gray_color < 128) {
1657 text_color = 0x00FFFFFF & ~text_color ;
1662 bool update_background_brush() {
1665 if(!DeleteObject(back_brush_)) {
1669 back_brush_ = CreateSolidBrush(back_color_) ;
1670 if(back_brush_ == NULL) {
1674 if(!SetClassLongPtr(
1675 hwnd_, GCLP_HBRBACKGROUND,
1676 reinterpret_cast<LONG_PTR
>(back_brush_))) {
1684 unsigned int FluentTray::message_id_ ;
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
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.