From da1af23de70e65bb8418a6fab5969ba22c665232 Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Thu, 11 May 2023 16:32:15 -0400 Subject: [PATCH 1/6] dd menus This commit adds menus to maxGUI --- .../3-ControlGalleryExample/EntryPoint.cpp | 12 +++ Code/maxGUI/FormConcept.cpp | 1 + Code/maxGUI/FormConcept.hpp | 53 +++++++++++ Code/maxGUI/FormContainer.cpp | 19 +++- Code/maxGUI/Menu.cpp | 39 ++++++++ Code/maxGUI/Menu.hpp | 90 +++++++++++++++++++ Code/maxGUI/Menu.inl | 59 ++++++++++++ Code/maxGUI/maxGUI.hpp | 1 + Projects/VisualStudio/maxGUI/maxGUI.vcxproj | 18 ++++ .../maxGUI/maxGUI.vcxproj.filters | 15 ++++ 10 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 Code/maxGUI/Menu.cpp create mode 100644 Code/maxGUI/Menu.hpp create mode 100644 Code/maxGUI/Menu.inl diff --git a/Code/Examples/3-ControlGalleryExample/EntryPoint.cpp b/Code/Examples/3-ControlGalleryExample/EntryPoint.cpp index 75b6f1e..08aa68c 100644 --- a/Code/Examples/3-ControlGalleryExample/EntryPoint.cpp +++ b/Code/Examples/3-ControlGalleryExample/EntryPoint.cpp @@ -18,6 +18,15 @@ class CustomButtonBehavior { }; +class ExitMenuBehavior { +public: + + static void OnPressed() noexcept { + maxGUI::PostExitMessage(0); + } + +}; + class ControlGalleryForm { public: @@ -47,6 +56,9 @@ class ControlGalleryForm { form->AddControl>(max::Containers::MakeRectangle(25, 750, 300, 25), "Option 2"); form->AddControl>(max::Containers::MakeRectangle(25, 800, 300, 25), "Textbox"); + + auto file_menu = form->AppendMenu("&File"); + file_menu->AppendMenu>("E&xit"); } void OnClosed(maxGUI::FormConcept* /*form*/) noexcept { diff --git a/Code/maxGUI/FormConcept.cpp b/Code/maxGUI/FormConcept.cpp index af0637a..a875921 100644 --- a/Code/maxGUI/FormConcept.cpp +++ b/Code/maxGUI/FormConcept.cpp @@ -11,6 +11,7 @@ namespace maxGUI { #if defined(MAX_PLATFORM_WINDOWS) FormConcept::FormConcept(HWND window_handle) noexcept : window_handle_(std::move(window_handle)) + , menu_bar_handle_(static_cast(INVALID_HANDLE_VALUE)) {} #elif defined(MAX_PLATFORM_LINUX) FormConcept::FormConcept(int width, int height, std::string title, FormStyles styles) noexcept diff --git a/Code/maxGUI/FormConcept.hpp b/Code/maxGUI/FormConcept.hpp index c822a87..3d11535 100644 --- a/Code/maxGUI/FormConcept.hpp +++ b/Code/maxGUI/FormConcept.hpp @@ -10,12 +10,16 @@ #include #include +#include #if defined(MAX_PLATFORM_WINDOWS) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif + #include + + #include #elif defined(MAX_PLATFORM_LINUX) #include @@ -24,6 +28,21 @@ #include #endif +namespace { + + // TODO: Use max's Exists here + template< typename T > + struct HasOnPressed { + typedef char yes[1]; + typedef char no[2]; + + template static yes& test(typename std::enable_if, bool>::type = 0); + template static no& test(...); + static bool const value = sizeof(test::type>(0)) == sizeof(yes&); + }; + +} // anonymous namespace + namespace maxGUI { @@ -44,6 +63,37 @@ namespace maxGUI virtual LRESULT OnWindowMessage(FormConcept* form, UINT message, WPARAM wparam, LPARAM lparam) noexcept = 0; #endif + template< typename T, typename... Params > + T* AppendMenu(Params&&... params) noexcept { +#if defined(MAX_PLATFORM_WINDOWS) + bool is_first_menu = menus_size() == 0; + if (is_first_menu) { + menu_bar_handle_ = CreateMenu(); + + MENUINFO menu_info = { 0 }; + menu_info.cbSize = sizeof(menu_info); + menu_info.fMask = MIM_STYLE; + menu_info.dwStyle = MNS_NOTIFYBYPOS; + SetMenuInfo(menu_bar_handle_, &menu_info); + + ::SetMenu(window_handle_, menu_bar_handle_); + } + + HMENU menu_handle = T::Create(menu_bar_handle_, std::forward(params)...); + auto menu_ptr = std::make_unique(std::move(menu_handle)); + T* raw_menu_ptr = menu_ptr.get(); + menus_.push_back(std::move(menu_ptr)); + + + if (is_first_menu) { + // TODO: This is only required if the window has already been drawn. IE the menus were added after WM_CREATE + DrawMenuBar(window_handle_); + } +#endif + + return raw_menu_ptr; + } + template T* AddControl(Params&&... params) noexcept { #if defined(MAX_PLATFORM_WINDOWS) @@ -65,10 +115,13 @@ namespace maxGUI #if defined(MAX_PLATFORM_WINDOWS) HWND window_handle_; + HMENU menu_bar_handle_; #elif defined(MAX_PLATFORM_LINUX) QWidget window_; #endif + std::vector> controls_; + std::vector> menus_; }; diff --git a/Code/maxGUI/FormContainer.cpp b/Code/maxGUI/FormContainer.cpp index 0dd18b0..057016d 100644 --- a/Code/maxGUI/FormContainer.cpp +++ b/Code/maxGUI/FormContainer.cpp @@ -98,12 +98,29 @@ namespace maxGUI { case WM_NCDESTROY: RemoveProp(window_handle, maxgui_formconcept_property_name); return 0; + case WM_MENUCOMMAND: + { + MENUITEMINFO menu_item_info; + menu_item_info.cbSize = sizeof(MENUITEMINFO); + menu_item_info.fMask = MIIM_DATA | MIIM_ID; + BOOL result = GetMenuItemInfo(reinterpret_cast(lparam), static_cast(wparam), TRUE, &menu_item_info); + if (result == 0) { + // error + } + + typedef void (*OnPressedType)(); + OnPressedType on_pressed = reinterpret_cast(menu_item_info.dwItemData); + if (on_pressed != nullptr) { + on_pressed(); + } + return 0; + } case WM_COMMAND: { auto form = GetFormFromHWND(window_handle); if (HIWORD(wparam) == 0 && lparam == 0) // menu { - //auto menu_identifier = LOWORD(wparam); + //auto menu_id = LOWORD(wparam); } else if (HIWORD(wparam) == 1 && lparam == 0) { // accelerator //auto accelerator_identifier = LOWORD(wparam); } else { diff --git a/Code/maxGUI/Menu.cpp b/Code/maxGUI/Menu.cpp new file mode 100644 index 0000000..561c34e --- /dev/null +++ b/Code/maxGUI/Menu.cpp @@ -0,0 +1,39 @@ +// Copyright 2024, The maxGUI Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include + +namespace maxGUI +{ + +#if defined(MAX_PLATFORM_WINDOWS) + Menu::Menu(HMENU menu_handle) noexcept + : menu_handle_(std::move(menu_handle)) + {} +#endif + +#if defined(MAX_PLATFORM_WINDOWS) + ParentMenu::ParentMenu(HMENU menu_handle) noexcept + : Menu(std::move(menu_handle)) + , next_submenu_id_(0) + {} +#endif + +#if defined(MAX_PLATFORM_WINDOWS) + HMENU ParentMenu::Create(HMENU parent_menu, std::string text) noexcept { + HMENU menu_handle = CreateMenu(); + + auto win32_text = Utf8ToWin32String(std::move(text)); + BOOL result = ::AppendMenu(parent_menu, MF_POPUP | MF_STRING, reinterpret_cast(menu_handle), win32_text.text_); + if (result == 0) { + // error + } + + return menu_handle; + } +#endif + +} \ No newline at end of file diff --git a/Code/maxGUI/Menu.hpp b/Code/maxGUI/Menu.hpp new file mode 100644 index 0000000..5c30e8d --- /dev/null +++ b/Code/maxGUI/Menu.hpp @@ -0,0 +1,90 @@ +// Copyright 2024, The maxGUI Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MAXGUI_MENU_HPP +#define MAXGUI_MENU_HPP + + +#include +#include + +#if defined(MAX_PLATFORM_WINDOWS) + +#include +#include +#include + +#if defined(MAX_PLATFORM_WINDOWS) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + + #include +#endif + + +namespace maxGUI +{ + + class Menu { + public: + +#if defined(MAX_PLATFORM_WINDOWS) + explicit Menu(HMENU menu_handle) noexcept; +#endif + + virtual ~Menu() noexcept = default; + + protected: + +#if defined(MAX_PLATFORM_WINDOWS) + HMENU menu_handle_; +#endif + + }; + + class ParentMenu : public Menu + { + public: + + explicit ParentMenu(HMENU menu_handle) noexcept; + + ~ParentMenu() noexcept override = default; + +#if defined(MAX_PLATFORM_WINDOWS) + static HMENU Create(HMENU parent_menu, std::string text) noexcept; +#endif + + template + T* AppendMenu(Params&&... params) noexcept; + + std::vector> submenus_; + + ULONG_PTR next_submenu_id_; + + }; + + class DefaultMenuBehavior { + }; + + template< class Behavior = DefaultMenuBehavior > + class PressableMenu : public Menu + { + public: + + explicit PressableMenu(HMENU menu_handle) noexcept; + + ~PressableMenu() noexcept override = default; + + static HMENU Create(HMENU parent_menu, ULONG_PTR id, std::string text) noexcept; + + }; + +} // namespace maxGUI + +#endif // #if defined(MAX_PLATFORM_WINDOWS) + +#include + +#endif // #ifndef MAXGUI_MENU_HPP \ No newline at end of file diff --git a/Code/maxGUI/Menu.inl b/Code/maxGUI/Menu.inl new file mode 100644 index 0000000..9c83073 --- /dev/null +++ b/Code/maxGUI/Menu.inl @@ -0,0 +1,59 @@ +// Copyright 2024, The maxGUI Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include + +namespace maxGUI +{ + + template + T* ParentMenu::AppendMenu(Params&&... params) noexcept { +#if defined(MAX_PLATFORM_WINDOWS) + HMENU menu_handle = T::Create(menu_handle_, next_submenu_id_, std::forward(params)...); + auto menu_ptr = std::make_unique(menu_handle); + + next_submenu_id_++; + + T* raw_menu_ptr = menu_ptr.get(); + submenus_.push_back(std::move(menu_ptr)); +#endif + return raw_menu_ptr; + } + + template< class Behavior > + PressableMenu< Behavior >::PressableMenu(HMENU menu_handle) noexcept + : Menu(std::move(menu_handle)) + {} + + template< class Behavior > + HMENU PressableMenu< Behavior >::Create(HMENU parent_menu, ULONG_PTR id, std::string text) noexcept { + HMENU submenu_handle = CreatePopupMenu(); + + auto win32_text = Utf8ToWin32String(std::move(text)); + BOOL result = ::AppendMenu(parent_menu, MF_STRING, reinterpret_cast(submenu_handle), win32_text.text_); + if (result == 0) { + // error + } + + MENUITEMINFO menu_item_info = { 0 }; + menu_item_info.cbSize = sizeof(menu_item_info); + //menu_item_info.fMask = MIIM_DATA | MIIM_STRING | MIIM_ID;// | MIIM_TYPE; + //menu_item_info.fMask = MIIM_DATA | MIIM_TYPE | MIIM_ID; + menu_item_info.fMask = MIIM_DATA | MIIM_TYPE;// | MIIM_ID; + menu_item_info.fType = MFT_STRING; + menu_item_info.dwTypeData = win32_text.text_; + menu_item_info.cch = win32_text.char_count_; + menu_item_info.dwItemData = reinterpret_cast(&Behavior::OnPressed); + + result = SetMenuItemInfo(parent_menu, id, TRUE, &menu_item_info); + if (result == 0) { + // error + } + + return submenu_handle; + } + +} // namespace maxGUI \ No newline at end of file diff --git a/Code/maxGUI/maxGUI.hpp b/Code/maxGUI/maxGUI.hpp index 36490a3..c97a99b 100644 --- a/Code/maxGUI/maxGUI.hpp +++ b/Code/maxGUI/maxGUI.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/Projects/VisualStudio/maxGUI/maxGUI.vcxproj b/Projects/VisualStudio/maxGUI/maxGUI.vcxproj index 8fe63e1..3da9a3a 100644 --- a/Projects/VisualStudio/maxGUI/maxGUI.vcxproj +++ b/Projects/VisualStudio/maxGUI/maxGUI.vcxproj @@ -1,5 +1,6 @@  + Debug @@ -30,6 +31,7 @@ + @@ -44,6 +46,7 @@ + @@ -64,6 +67,7 @@ + @@ -94,6 +98,7 @@ + @@ -121,6 +126,11 @@ + + + Designer + + {3A9E3E87-9F00-4240-8E5D-489DC37C73DA} Win32Proj @@ -288,5 +298,13 @@ + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/Projects/VisualStudio/maxGUI/maxGUI.vcxproj.filters b/Projects/VisualStudio/maxGUI/maxGUI.vcxproj.filters index c0aa93c..605d58d 100644 --- a/Projects/VisualStudio/maxGUI/maxGUI.vcxproj.filters +++ b/Projects/VisualStudio/maxGUI/maxGUI.vcxproj.filters @@ -119,6 +119,10 @@ Code + + Code + + @@ -192,6 +196,9 @@ Code + + Code + @@ -347,5 +354,13 @@ Code + + Code + + + + + Docs + \ No newline at end of file From 0687edee4c43d39e9235c67573e024e62119d0c3 Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Tue, 29 Oct 2024 19:10:09 -0400 Subject: [PATCH 2/6] Hopefully fix linux builds --- Code/maxGUI/Menu.inl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Code/maxGUI/Menu.inl b/Code/maxGUI/Menu.inl index 9c83073..15f92ad 100644 --- a/Code/maxGUI/Menu.inl +++ b/Code/maxGUI/Menu.inl @@ -4,6 +4,8 @@ #include +#if defined(MAX_PLATFORM_WINDOWS) + #include namespace maxGUI @@ -56,4 +58,6 @@ namespace maxGUI return submenu_handle; } -} // namespace maxGUI \ No newline at end of file +} // namespace maxGUI + +#endif \ No newline at end of file From a43b26f4fb586ff40228b16a42609973848b7f71 Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Tue, 29 Oct 2024 19:15:49 -0400 Subject: [PATCH 3/6] Add Linux codepath for FormConcept::AppendMenu --- Code/maxGUI/FormConcept.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Code/maxGUI/FormConcept.hpp b/Code/maxGUI/FormConcept.hpp index 3d11535..97aeb99 100644 --- a/Code/maxGUI/FormConcept.hpp +++ b/Code/maxGUI/FormConcept.hpp @@ -66,7 +66,7 @@ namespace maxGUI template< typename T, typename... Params > T* AppendMenu(Params&&... params) noexcept { #if defined(MAX_PLATFORM_WINDOWS) - bool is_first_menu = menus_size() == 0; + bool is_first_menu = menus_.size() == 0; if (is_first_menu) { menu_bar_handle_ = CreateMenu(); @@ -89,9 +89,12 @@ namespace maxGUI // TODO: This is only required if the window has already been drawn. IE the menus were added after WM_CREATE DrawMenuBar(window_handle_); } -#endif return raw_menu_ptr; +#else + // TODO: Implement on other platforms + return nullptr; +#endif } template From 3ec4188d1c1ea912c2366ffd8d2c590e4c29b262 Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Tue, 29 Oct 2024 19:23:54 -0400 Subject: [PATCH 4/6] Remove FormConcept::menus_ from Linux builds --- Code/maxGUI/FormConcept.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/maxGUI/FormConcept.hpp b/Code/maxGUI/FormConcept.hpp index 97aeb99..420909d 100644 --- a/Code/maxGUI/FormConcept.hpp +++ b/Code/maxGUI/FormConcept.hpp @@ -124,7 +124,9 @@ namespace maxGUI #endif std::vector> controls_; +#if defined(MAX_PLATFORM_WINDOWS) std::vector> menus_; +#endif }; From 123e92e02876134d5fabdbfe3ac5d6baf84885e5 Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Tue, 29 Oct 2024 19:34:21 -0400 Subject: [PATCH 5/6] Update NuGet packages --- Projects/VisualStudio/maxGUI/packages.config | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Projects/VisualStudio/maxGUI/packages.config diff --git a/Projects/VisualStudio/maxGUI/packages.config b/Projects/VisualStudio/maxGUI/packages.config new file mode 100644 index 0000000..f8cce01 --- /dev/null +++ b/Projects/VisualStudio/maxGUI/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From f1e97d6f1f1c12692b09df6807cce3a8958accb8 Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Tue, 29 Oct 2024 19:36:25 -0400 Subject: [PATCH 6/6] Update build script to update NuGet packages --- .github/workflows/build-and-test.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index d68352c..a4b2d68 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -25,6 +25,9 @@ jobs: - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1.0.2 + - name: Update NuGet packages + uses: nuget update + - name: Build working-directory: ${{env.GITHUB_WORKSPACE}} run: msbuild /m /p:Configuration=${{ matrix.configuration }} /p:Platform=${{ matrix.platform }} .\Projects\VisualStudio