Skip to content

Commit abe0cc4

Browse files
authored
Add menus (#80)
Add menus This commit adds menus to maxGUI
1 parent c0bb810 commit abe0cc4

File tree

12 files changed

+324
-1
lines changed

12 files changed

+324
-1
lines changed

.github/workflows/build-and-test.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ jobs:
2525
- name: Add MSBuild to PATH
2626
uses: microsoft/setup-msbuild@v1.0.2
2727

28+
- name: Update NuGet packages
29+
uses: nuget update
30+
2831
- name: Build
2932
working-directory: ${{env.GITHUB_WORKSPACE}}
3033
run: msbuild /m /p:Configuration=${{ matrix.configuration }} /p:Platform=${{ matrix.platform }} .\Projects\VisualStudio

Code/Examples/3-ControlGalleryExample/EntryPoint.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ class CustomButtonBehavior {
1818

1919
};
2020

21+
class ExitMenuBehavior {
22+
public:
23+
24+
static void OnPressed() noexcept {
25+
maxGUI::PostExitMessage(0);
26+
}
27+
28+
};
29+
2130
class ControlGalleryForm {
2231
public:
2332

@@ -47,6 +56,9 @@ class ControlGalleryForm {
4756
form->AddControl<maxGUI::RadioButton<>>(max::Containers::MakeRectangle(25, 750, 300, 25), "Option 2");
4857

4958
form->AddControl<maxGUI::TextBox<>>(max::Containers::MakeRectangle(25, 800, 300, 25), "Textbox");
59+
60+
auto file_menu = form->AppendMenu<maxGUI::ParentMenu>("&File");
61+
file_menu->AppendMenu<maxGUI::PressableMenu<ExitMenuBehavior>>("E&xit");
5062
}
5163

5264
void OnClosed(maxGUI::FormConcept* /*form*/) noexcept {

Code/maxGUI/FormConcept.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace maxGUI {
1111
#if defined(MAX_PLATFORM_WINDOWS)
1212
FormConcept::FormConcept(HWND window_handle) noexcept
1313
: window_handle_(std::move(window_handle))
14+
, menu_bar_handle_(static_cast<HMENU>(INVALID_HANDLE_VALUE))
1415
{}
1516
#elif defined(MAX_PLATFORM_LINUX)
1617
FormConcept::FormConcept(int width, int height, std::string title, FormStyles styles) noexcept

Code/maxGUI/FormConcept.hpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@
1010

1111
#include <max/Compiling/Configuration.hpp>
1212
#include <maxGUI/Control.hpp>
13+
#include <maxGUI/Menu.hpp>
1314

1415
#if defined(MAX_PLATFORM_WINDOWS)
1516
#ifndef WIN32_LEAN_AND_MEAN
1617
#define WIN32_LEAN_AND_MEAN
1718
#endif
19+
1820
#include <Windows.h>
21+
22+
#include <maxGUI/Win32String.hpp>
1923
#elif defined(MAX_PLATFORM_LINUX)
2024
#include <string>
2125

@@ -24,6 +28,21 @@
2428
#include <maxGUI/FormStyles.hpp>
2529
#endif
2630

31+
namespace {
32+
33+
// TODO: Use max's Exists here
34+
template< typename T >
35+
struct HasOnPressed {
36+
typedef char yes[1];
37+
typedef char no[2];
38+
39+
template <typename U> static yes& test(typename std::enable_if<std::is_function_v<decltype(U::OnPressed)>, bool>::type = 0);
40+
template <typename U> static no& test(...);
41+
static bool const value = sizeof(test<typename std::remove_cv<T>::type>(0)) == sizeof(yes&);
42+
};
43+
44+
} // anonymous namespace
45+
2746
namespace maxGUI
2847
{
2948

@@ -44,6 +63,40 @@ namespace maxGUI
4463
virtual LRESULT OnWindowMessage(FormConcept* form, UINT message, WPARAM wparam, LPARAM lparam) noexcept = 0;
4564
#endif
4665

66+
template< typename T, typename... Params >
67+
T* AppendMenu(Params&&... params) noexcept {
68+
#if defined(MAX_PLATFORM_WINDOWS)
69+
bool is_first_menu = menus_.size() == 0;
70+
if (is_first_menu) {
71+
menu_bar_handle_ = CreateMenu();
72+
73+
MENUINFO menu_info = { 0 };
74+
menu_info.cbSize = sizeof(menu_info);
75+
menu_info.fMask = MIM_STYLE;
76+
menu_info.dwStyle = MNS_NOTIFYBYPOS;
77+
SetMenuInfo(menu_bar_handle_, &menu_info);
78+
79+
::SetMenu(window_handle_, menu_bar_handle_);
80+
}
81+
82+
HMENU menu_handle = T::Create(menu_bar_handle_, std::forward<Params>(params)...);
83+
auto menu_ptr = std::make_unique<T>(std::move(menu_handle));
84+
T* raw_menu_ptr = menu_ptr.get();
85+
menus_.push_back(std::move(menu_ptr));
86+
87+
88+
if (is_first_menu) {
89+
// TODO: This is only required if the window has already been drawn. IE the menus were added after WM_CREATE
90+
DrawMenuBar(window_handle_);
91+
}
92+
93+
return raw_menu_ptr;
94+
#else
95+
// TODO: Implement on other platforms
96+
return nullptr;
97+
#endif
98+
}
99+
47100
template<typename T, typename... Params>
48101
T* AddControl(Params&&... params) noexcept {
49102
#if defined(MAX_PLATFORM_WINDOWS)
@@ -65,10 +118,15 @@ namespace maxGUI
65118

66119
#if defined(MAX_PLATFORM_WINDOWS)
67120
HWND window_handle_;
121+
HMENU menu_bar_handle_;
68122
#elif defined(MAX_PLATFORM_LINUX)
69123
QWidget window_;
70124
#endif
125+
71126
std::vector<std::unique_ptr<Control>> controls_;
127+
#if defined(MAX_PLATFORM_WINDOWS)
128+
std::vector<std::unique_ptr<Menu>> menus_;
129+
#endif
72130

73131
};
74132

Code/maxGUI/FormContainer.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,29 @@ namespace maxGUI {
9898
case WM_NCDESTROY:
9999
RemoveProp(window_handle, maxgui_formconcept_property_name);
100100
return 0;
101+
case WM_MENUCOMMAND:
102+
{
103+
MENUITEMINFO menu_item_info;
104+
menu_item_info.cbSize = sizeof(MENUITEMINFO);
105+
menu_item_info.fMask = MIIM_DATA | MIIM_ID;
106+
BOOL result = GetMenuItemInfo(reinterpret_cast<HMENU>(lparam), static_cast<UINT>(wparam), TRUE, &menu_item_info);
107+
if (result == 0) {
108+
// error
109+
}
110+
111+
typedef void (*OnPressedType)();
112+
OnPressedType on_pressed = reinterpret_cast<OnPressedType>(menu_item_info.dwItemData);
113+
if (on_pressed != nullptr) {
114+
on_pressed();
115+
}
116+
return 0;
117+
}
101118
case WM_COMMAND:
102119
{
103120
auto form = GetFormFromHWND(window_handle);
104121
if (HIWORD(wparam) == 0 && lparam == 0) // menu
105122
{
106-
//auto menu_identifier = LOWORD(wparam);
123+
//auto menu_id = LOWORD(wparam);
107124
} else if (HIWORD(wparam) == 1 && lparam == 0) { // accelerator
108125
//auto accelerator_identifier = LOWORD(wparam);
109126
} else {

Code/maxGUI/Menu.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2024, The maxGUI Contributors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include <maxGUI/Menu.hpp>
6+
7+
#include <utility>
8+
9+
namespace maxGUI
10+
{
11+
12+
#if defined(MAX_PLATFORM_WINDOWS)
13+
Menu::Menu(HMENU menu_handle) noexcept
14+
: menu_handle_(std::move(menu_handle))
15+
{}
16+
#endif
17+
18+
#if defined(MAX_PLATFORM_WINDOWS)
19+
ParentMenu::ParentMenu(HMENU menu_handle) noexcept
20+
: Menu(std::move(menu_handle))
21+
, next_submenu_id_(0)
22+
{}
23+
#endif
24+
25+
#if defined(MAX_PLATFORM_WINDOWS)
26+
HMENU ParentMenu::Create(HMENU parent_menu, std::string text) noexcept {
27+
HMENU menu_handle = CreateMenu();
28+
29+
auto win32_text = Utf8ToWin32String(std::move(text));
30+
BOOL result = ::AppendMenu(parent_menu, MF_POPUP | MF_STRING, reinterpret_cast<UINT_PTR>(menu_handle), win32_text.text_);
31+
if (result == 0) {
32+
// error
33+
}
34+
35+
return menu_handle;
36+
}
37+
#endif
38+
39+
}

Code/maxGUI/Menu.hpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright 2024, The maxGUI Contributors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef MAXGUI_MENU_HPP
6+
#define MAXGUI_MENU_HPP
7+
8+
9+
#include <max/Compiling/Configuration.hpp>
10+
#include <max/Containers/Rectangle.hpp>
11+
12+
#if defined(MAX_PLATFORM_WINDOWS)
13+
14+
#include <memory>
15+
#include <string>
16+
#include <vector>
17+
18+
#if defined(MAX_PLATFORM_WINDOWS)
19+
#ifndef WIN32_LEAN_AND_MEAN
20+
#define WIN32_LEAN_AND_MEAN
21+
#endif
22+
23+
#include <Windows.h>
24+
#endif
25+
26+
27+
namespace maxGUI
28+
{
29+
30+
class Menu {
31+
public:
32+
33+
#if defined(MAX_PLATFORM_WINDOWS)
34+
explicit Menu(HMENU menu_handle) noexcept;
35+
#endif
36+
37+
virtual ~Menu() noexcept = default;
38+
39+
protected:
40+
41+
#if defined(MAX_PLATFORM_WINDOWS)
42+
HMENU menu_handle_;
43+
#endif
44+
45+
};
46+
47+
class ParentMenu : public Menu
48+
{
49+
public:
50+
51+
explicit ParentMenu(HMENU menu_handle) noexcept;
52+
53+
~ParentMenu() noexcept override = default;
54+
55+
#if defined(MAX_PLATFORM_WINDOWS)
56+
static HMENU Create(HMENU parent_menu, std::string text) noexcept;
57+
#endif
58+
59+
template<typename T, typename... Params>
60+
T* AppendMenu(Params&&... params) noexcept;
61+
62+
std::vector<std::unique_ptr<Menu>> submenus_;
63+
64+
ULONG_PTR next_submenu_id_;
65+
66+
};
67+
68+
class DefaultMenuBehavior {
69+
};
70+
71+
template< class Behavior = DefaultMenuBehavior >
72+
class PressableMenu : public Menu
73+
{
74+
public:
75+
76+
explicit PressableMenu(HMENU menu_handle) noexcept;
77+
78+
~PressableMenu() noexcept override = default;
79+
80+
static HMENU Create(HMENU parent_menu, ULONG_PTR id, std::string text) noexcept;
81+
82+
};
83+
84+
} // namespace maxGUI
85+
86+
#endif // #if defined(MAX_PLATFORM_WINDOWS)
87+
88+
#include <maxGUI/Menu.inl>
89+
90+
#endif // #ifndef MAXGUI_MENU_HPP

Code/maxGUI/Menu.inl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2024, The maxGUI Contributors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include <utility>
6+
7+
#if defined(MAX_PLATFORM_WINDOWS)
8+
9+
#include <maxGUI/Win32String.hpp>
10+
11+
namespace maxGUI
12+
{
13+
14+
template<typename T, typename... Params>
15+
T* ParentMenu::AppendMenu(Params&&... params) noexcept {
16+
#if defined(MAX_PLATFORM_WINDOWS)
17+
HMENU menu_handle = T::Create(menu_handle_, next_submenu_id_, std::forward<Params>(params)...);
18+
auto menu_ptr = std::make_unique<T>(menu_handle);
19+
20+
next_submenu_id_++;
21+
22+
T* raw_menu_ptr = menu_ptr.get();
23+
submenus_.push_back(std::move(menu_ptr));
24+
#endif
25+
return raw_menu_ptr;
26+
}
27+
28+
template< class Behavior >
29+
PressableMenu< Behavior >::PressableMenu(HMENU menu_handle) noexcept
30+
: Menu(std::move(menu_handle))
31+
{}
32+
33+
template< class Behavior >
34+
HMENU PressableMenu< Behavior >::Create(HMENU parent_menu, ULONG_PTR id, std::string text) noexcept {
35+
HMENU submenu_handle = CreatePopupMenu();
36+
37+
auto win32_text = Utf8ToWin32String(std::move(text));
38+
BOOL result = ::AppendMenu(parent_menu, MF_STRING, reinterpret_cast<UINT_PTR>(submenu_handle), win32_text.text_);
39+
if (result == 0) {
40+
// error
41+
}
42+
43+
MENUITEMINFO menu_item_info = { 0 };
44+
menu_item_info.cbSize = sizeof(menu_item_info);
45+
//menu_item_info.fMask = MIIM_DATA | MIIM_STRING | MIIM_ID;// | MIIM_TYPE;
46+
//menu_item_info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID;
47+
menu_item_info.fMask = MIIM_DATA | MIIM_TYPE;// | MIIM_ID;
48+
menu_item_info.fType = MFT_STRING;
49+
menu_item_info.dwTypeData = win32_text.text_;
50+
menu_item_info.cch = win32_text.char_count_;
51+
menu_item_info.dwItemData = reinterpret_cast<ULONG_PTR>(&Behavior::OnPressed);
52+
53+
result = SetMenuItemInfo(parent_menu, id, TRUE, &menu_item_info);
54+
if (result == 0) {
55+
// error
56+
}
57+
58+
return submenu_handle;
59+
}
60+
61+
} // namespace maxGUI
62+
63+
#endif

Code/maxGUI/maxGUI.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <maxGUI/Frame.hpp>
1717
#include <maxGUI/Label.hpp>
1818
#include <maxGUI/ListBox.hpp>
19+
#include <maxGUI/Menu.hpp>
1920
#include <maxGUI/MultilineTextBox.hpp>
2021
#include <maxGUI/ProgressBar.hpp>
2122
#include <maxGUI/RadioButton.hpp>

0 commit comments

Comments
 (0)