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.