File: menu.cpp

package info (click to toggle)
higan 098-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 11,904 kB
  • ctags: 13,286
  • sloc: cpp: 108,285; ansic: 778; makefile: 32; sh: 18
file content (123 lines) | stat: -rw-r--r-- 3,821 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#if defined(Hiro_Menu)

namespace hiro {

auto pMenu::construct() -> void {
  _createBitmap();
}

auto pMenu::destruct() -> void {
  if(hbitmap) { DeleteObject(hbitmap); hbitmap = nullptr; }
  if(hmenu) { DestroyMenu(hmenu); hmenu = nullptr; }
}

auto pMenu::append(sAction action) -> void {
  _synchronize();
}

auto pMenu::remove(sAction action) -> void {
  _synchronize();
}

auto pMenu::setIcon(const image& icon) -> void {
  _createBitmap();
  _synchronize();
}

auto pMenu::setText(const string& text) -> void {
  _synchronize();
}

auto pMenu::_createBitmap() -> void {
  if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; }

  if(auto icon = state().icon) {
    icon.alphaBlend(GetSysColor(COLOR_MENU));  //Windows does not alpha blend menu icons properly (leaves black outline)
    icon.scale(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), Interpolation::Linear);
    hbitmap = CreateBitmap(icon);
  }
}

//Windows actions lack the ability to toggle visibility.
//To support this, menus must be destroyed and recreated when toggling any action's visibility.
auto pMenu::_update() -> void {
  if(hmenu) DestroyMenu(hmenu);
  hmenu = CreatePopupMenu();

  MENUINFO mi{sizeof(MENUINFO)};
  mi.fMask = MIM_STYLE;
  mi.dwStyle = MNS_NOTIFYBYPOS;  //| MNS_MODELESS;
  SetMenuInfo(hmenu, &mi);

  unsigned position = 0;

  for(auto& action : state().actions) {
    if(!action->self()) continue;
    action->self()->position = position;
    unsigned enabled = action->enabled() ? 0 : MF_GRAYED;

    MENUITEMINFO mii{sizeof(MENUITEMINFO)};
    mii.fMask = MIIM_DATA;
    mii.dwItemData = (ULONG_PTR)action.data();

    if(auto menu = dynamic_cast<mMenu*>(action.data())) {
      if(menu->visible()) {
        menu->self()->_update();
        AppendMenu(hmenu, MF_STRING | MF_POPUP | enabled, (UINT_PTR)menu->self()->hmenu, utf16_t(menu->text()));
        if(auto bitmap = menu->self()->hbitmap) {
          //Windows XP and below displays MIIM_BITMAP + hbmpItem in its own column (separate from check/radio marks)
          //this causes too much spacing, so use a custom checkmark image instead
          mii.fMask |= MIIM_CHECKMARKS;
          mii.hbmpUnchecked = bitmap;
        }
        SetMenuItemInfo(hmenu, position++, true, &mii);
      }
    }

    #if defined(Hiro_MenuSeparator)
    else if(auto menuSeparator = dynamic_cast<mMenuSeparator*>(action.data())) {
      if(menuSeparator->visible()) {
        AppendMenu(hmenu, MF_SEPARATOR | enabled, position, L"");
        SetMenuItemInfo(hmenu, position++, true, &mii);
      }
    }
    #endif

    #if defined(Hiro_MenuItem)
    else if(auto menuItem = dynamic_cast<mMenuItem*>(action.data())) {
      if(menuItem->visible()) {
        AppendMenu(hmenu, MF_STRING | enabled, position, utf16_t(menuItem->text()));
        if(auto bitmap = menuItem->self()->hbitmap) {
          mii.fMask |= MIIM_CHECKMARKS;
          mii.hbmpUnchecked = bitmap;
        }
        SetMenuItemInfo(hmenu, position++, true, &mii);
      }
    }
    #endif

    #if defined(Hiro_MenuCheckItem)
    else if(auto menuCheckItem = dynamic_cast<mMenuCheckItem*>(action.data())) {
      if(menuCheckItem->visible()) {
        AppendMenu(hmenu, MF_STRING | enabled, position, utf16_t(menuCheckItem->text()));
        SetMenuItemInfo(hmenu, position++, true, &mii);
        if(menuCheckItem->checked()) menuCheckItem->setChecked();
      }
    }
    #endif

    #if defined(Hiro_MenuRadioItem)
    else if(auto menuRadioItem = dynamic_cast<mMenuRadioItem*>(action.data())) {
      if(menuRadioItem->visible()) {
        AppendMenu(hmenu, MF_STRING | enabled, position, utf16_t(menuRadioItem->text()));
        SetMenuItemInfo(hmenu, position++, true, &mii);
        if(menuRadioItem->checked()) menuRadioItem->setChecked();
      }
    }
    #endif
  }
}

}

#endif