Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add menus #80

Merged
merged 6 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ jobs:
- name: Add MSBuild to PATH
uses: microsoft/[email protected]

- 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
Expand Down
12 changes: 12 additions & 0 deletions Code/Examples/3-ControlGalleryExample/EntryPoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ class CustomButtonBehavior {

};

class ExitMenuBehavior {
public:

static void OnPressed() noexcept {
maxGUI::PostExitMessage(0);
}

};

class ControlGalleryForm {
public:

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

form->AddControl<maxGUI::TextBox<>>(max::Containers::MakeRectangle(25, 800, 300, 25), "Textbox");

auto file_menu = form->AppendMenu<maxGUI::ParentMenu>("&File");
file_menu->AppendMenu<maxGUI::PressableMenu<ExitMenuBehavior>>("E&xit");
}

void OnClosed(maxGUI::FormConcept* /*form*/) noexcept {
Expand Down
1 change: 1 addition & 0 deletions Code/maxGUI/FormConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<HMENU>(INVALID_HANDLE_VALUE))
{}
#elif defined(MAX_PLATFORM_LINUX)
FormConcept::FormConcept(int width, int height, std::string title, FormStyles styles) noexcept
Expand Down
58 changes: 58 additions & 0 deletions Code/maxGUI/FormConcept.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@

#include <max/Compiling/Configuration.hpp>
#include <maxGUI/Control.hpp>
#include <maxGUI/Menu.hpp>

#if defined(MAX_PLATFORM_WINDOWS)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <Windows.h>

#include <maxGUI/Win32String.hpp>
#elif defined(MAX_PLATFORM_LINUX)
#include <string>

Expand All @@ -24,6 +28,21 @@
#include <maxGUI/FormStyles.hpp>
#endif

namespace {

// TODO: Use max's Exists here
template< typename T >
struct HasOnPressed {
typedef char yes[1];
typedef char no[2];

template <typename U> static yes& test(typename std::enable_if<std::is_function_v<decltype(U::OnPressed)>, bool>::type = 0);
template <typename U> static no& test(...);
static bool const value = sizeof(test<typename std::remove_cv<T>::type>(0)) == sizeof(yes&);
};

} // anonymous namespace

namespace maxGUI
{

Expand All @@ -44,6 +63,40 @@ 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>(params)...);
auto menu_ptr = std::make_unique<T>(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_);
}

return raw_menu_ptr;
#else
// TODO: Implement on other platforms
return nullptr;
#endif
}

template<typename T, typename... Params>
T* AddControl(Params&&... params) noexcept {
#if defined(MAX_PLATFORM_WINDOWS)
Expand All @@ -65,10 +118,15 @@ namespace maxGUI

#if defined(MAX_PLATFORM_WINDOWS)
HWND window_handle_;
HMENU menu_bar_handle_;
#elif defined(MAX_PLATFORM_LINUX)
QWidget window_;
#endif

std::vector<std::unique_ptr<Control>> controls_;
#if defined(MAX_PLATFORM_WINDOWS)
std::vector<std::unique_ptr<Menu>> menus_;
#endif

};

Expand Down
19 changes: 18 additions & 1 deletion Code/maxGUI/FormContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<HMENU>(lparam), static_cast<UINT>(wparam), TRUE, &menu_item_info);
if (result == 0) {
// error
}

typedef void (*OnPressedType)();
OnPressedType on_pressed = reinterpret_cast<OnPressedType>(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 {
Expand Down
39 changes: 39 additions & 0 deletions Code/maxGUI/Menu.cpp
Original file line number Diff line number Diff line change
@@ -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 <maxGUI/Menu.hpp>

#include <utility>

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<UINT_PTR>(menu_handle), win32_text.text_);
if (result == 0) {
// error
}

return menu_handle;
}
#endif

}
90 changes: 90 additions & 0 deletions Code/maxGUI/Menu.hpp
Original file line number Diff line number Diff line change
@@ -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 <max/Compiling/Configuration.hpp>
#include <max/Containers/Rectangle.hpp>

#if defined(MAX_PLATFORM_WINDOWS)

#include <memory>
#include <string>
#include <vector>

#if defined(MAX_PLATFORM_WINDOWS)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <Windows.h>
#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<typename T, typename... Params>
T* AppendMenu(Params&&... params) noexcept;

std::vector<std::unique_ptr<Menu>> 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 <maxGUI/Menu.inl>

#endif // #ifndef MAXGUI_MENU_HPP
63 changes: 63 additions & 0 deletions Code/maxGUI/Menu.inl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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 <utility>

#if defined(MAX_PLATFORM_WINDOWS)

#include <maxGUI/Win32String.hpp>

namespace maxGUI
{

template<typename T, typename... Params>
T* ParentMenu::AppendMenu(Params&&... params) noexcept {
#if defined(MAX_PLATFORM_WINDOWS)
HMENU menu_handle = T::Create(menu_handle_, next_submenu_id_, std::forward<Params>(params)...);
auto menu_ptr = std::make_unique<T>(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<UINT_PTR>(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<ULONG_PTR>(&Behavior::OnPressed);

result = SetMenuItemInfo(parent_menu, id, TRUE, &menu_item_info);
if (result == 0) {
// error
}

return submenu_handle;
}

} // namespace maxGUI

#endif
1 change: 1 addition & 0 deletions Code/maxGUI/maxGUI.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <maxGUI/Frame.hpp>
#include <maxGUI/Label.hpp>
#include <maxGUI/ListBox.hpp>
#include <maxGUI/Menu.hpp>
#include <maxGUI/MultilineTextBox.hpp>
#include <maxGUI/ProgressBar.hpp>
#include <maxGUI/RadioButton.hpp>
Expand Down
Loading