diff --git a/src/xenia/ui/main_window.cc b/src/xenia/ui/main_window.cc index 82e743a81..d1ac06260 100644 --- a/src/xenia/ui/main_window.cc +++ b/src/xenia/ui/main_window.cc @@ -19,7 +19,8 @@ namespace xe { namespace ui { MainWindow::MainWindow(Emulator* emulator) - : PlatformWindow(L"xenia"), emulator_(emulator) {} + : PlatformWindow(L"xenia"), emulator_(emulator), + main_menu_(MenuItem::Type::kNormal) {} MainWindow::~MainWindow() = default; @@ -64,6 +65,19 @@ bool MainWindow::Initialize() { } e.set_handled(handled); }); + + // Main menu + // FIXME: This code is really messy. + auto file = std::make_unique(MenuItem::Type::kPopup, L"&File"); + file->AddChild(std::make_unique( + MenuItem::Type::kString, Commands::IDC_FILE_OPEN, L"&Open")); + + main_menu_.AddChild(std::move(file)); + + auto debug = std::make_unique(MenuItem::Type::kPopup, L"&Debug"); + + SetMenu(&main_menu_); + return true; } @@ -75,6 +89,10 @@ void MainWindow::OnClose() { exit(1); } +void MainWindow::OnCommand(int id) { + +} + X_STATUS MainWindow::LaunchPath(std::wstring path) { X_STATUS result; diff --git a/src/xenia/ui/main_window.h b/src/xenia/ui/main_window.h index d8e9d739a..cfff35137 100644 --- a/src/xenia/ui/main_window.h +++ b/src/xenia/ui/main_window.h @@ -16,6 +16,7 @@ // TODO(benvanik): only on windows. #include "xenia/ui/win32/win32_loop.h" #include "xenia/ui/win32/win32_window.h" +#include "xenia/ui/win32/win32_menu_item.h" namespace xe { class Emulator; @@ -26,6 +27,7 @@ namespace ui { using PlatformLoop = xe::ui::win32::Win32Loop; using PlatformWindow = xe::ui::win32::Win32Window; +using PlatformMenu = xe::ui::win32::Win32MenuItem; class MainWindow : public PlatformWindow { public: @@ -43,9 +45,15 @@ class MainWindow : public PlatformWindow { bool Initialize(); void OnClose() override; + void OnCommand(int id) override; + + enum Commands { + IDC_FILE_OPEN, + }; Emulator* emulator_; PlatformLoop loop_; + PlatformMenu main_menu_; }; } // namespace ui diff --git a/src/xenia/ui/menu_item.cc b/src/xenia/ui/menu_item.cc index 86f9d525a..cd89ae4d7 100644 --- a/src/xenia/ui/menu_item.cc +++ b/src/xenia/ui/menu_item.cc @@ -14,6 +14,9 @@ namespace ui { MenuItem::MenuItem(Type type) : type_(type), parent_item_(nullptr) {} +MenuItem::MenuItem(Type type, const std::wstring &text) : + type_(type), parent_item_(nullptr), text_(text) {} + MenuItem::~MenuItem() = default; void MenuItem::AddChild(MenuItem* child_item) { diff --git a/src/xenia/ui/menu_item.h b/src/xenia/ui/menu_item.h index 72993540b..ec2c8ea07 100644 --- a/src/xenia/ui/menu_item.h +++ b/src/xenia/ui/menu_item.h @@ -19,20 +19,22 @@ namespace xe { namespace ui { -class Window; - class MenuItem { public: typedef std::unique_ptr MenuItemPtr; enum class Type { - kNormal, + kPopup, // Popup menu (submenu) kSeparator, + kNormal, // Root menu + kString, // Menu is just a string }; virtual ~MenuItem(); MenuItem* parent_item() const { return parent_item_; } + Type type() { return type_; } + const std::wstring &text() { return text_; } void AddChild(MenuItem* child_item); void AddChild(std::unique_ptr child_item); @@ -43,16 +45,17 @@ class MenuItem { protected: MenuItem(Type type); + MenuItem(Type type, const std::wstring &text); virtual void OnChildAdded(MenuItem* child_item) {} virtual void OnChildRemoved(MenuItem* child_item) {} virtual void OnSelected(UIEvent& e); - private: Type type_; MenuItem* parent_item_; std::vector children_; + std::wstring text_; // Text associated with this item (typically the title) }; } // namespace ui diff --git a/src/xenia/ui/win32/win32_menu_item.cc b/src/xenia/ui/win32/win32_menu_item.cc index c2371e39c..c7dbb7cbd 100644 --- a/src/xenia/ui/win32/win32_menu_item.cc +++ b/src/xenia/ui/win32/win32_menu_item.cc @@ -9,12 +9,29 @@ #include "xenia/ui/win32/win32_menu_item.h" +#include "xenia/base/assert.h" + namespace xe { namespace ui { namespace win32 { +Win32MenuItem::Win32MenuItem(Type type, int id, const std::wstring& text) + : id_(id), MenuItem(type, text) { + switch (type) { + case MenuItem::Type::kNormal: + handle_ = CreateMenu(); + break; + case MenuItem::Type::kPopup: + handle_ = CreatePopupMenu(); + break; + } +} + Win32MenuItem::Win32MenuItem(Type type) - : MenuItem(type), handle_(CreateMenu()) {} + : Win32MenuItem(type, 0, L"") {} + +Win32MenuItem::Win32MenuItem(Type type, const std::wstring& text) + : Win32MenuItem(type, 0, text) {} Win32MenuItem::~Win32MenuItem() { if (handle_) { @@ -24,7 +41,20 @@ Win32MenuItem::~Win32MenuItem() { void Win32MenuItem::OnChildAdded(MenuItem* generic_child_item) { auto child_item = static_cast(generic_child_item); - // + + switch (child_item->type()) { + case MenuItem::Type::kPopup: + AppendMenuW(handle_, MF_POPUP, (UINT)child_item->handle(), + child_item->text().c_str()); + break; + case MenuItem::Type::kSeparator: + AppendMenuW(handle_, MF_SEPARATOR, child_item->id(), 0); + break; + case MenuItem::Type::kString: + AppendMenuW(handle_, MF_STRING, child_item->id(), + child_item->text().c_str()); + break; + } } void Win32MenuItem::OnChildRemoved(MenuItem* generic_child_item) { diff --git a/src/xenia/ui/win32/win32_menu_item.h b/src/xenia/ui/win32/win32_menu_item.h index b592fbe7a..4e0e591c1 100644 --- a/src/xenia/ui/win32/win32_menu_item.h +++ b/src/xenia/ui/win32/win32_menu_item.h @@ -21,16 +21,23 @@ namespace win32 { class Win32MenuItem : public MenuItem { public: + Win32MenuItem(Type type); + Win32MenuItem(Type type, const std::wstring &text); + Win32MenuItem(Type type, int id, const std::wstring &text); ~Win32MenuItem() override; + HMENU handle() { return handle_; } + int id() { return id_; } + protected: void OnChildAdded(MenuItem* child_item) override; void OnChildRemoved(MenuItem* child_item) override; private: - Win32MenuItem(Type type); HMENU handle_; + uint32_t position_; // Position within parent, if any + int id_; }; } // namespace win32 diff --git a/src/xenia/ui/win32/win32_window.cc b/src/xenia/ui/win32/win32_window.cc index e3576de98..962051ac3 100644 --- a/src/xenia/ui/win32/win32_window.cc +++ b/src/xenia/ui/win32/win32_window.cc @@ -20,7 +20,9 @@ namespace ui { namespace win32 { Win32Window::Win32Window(const std::wstring& title) - : Window(title), main_menu_(nullptr), closing_(false) {} + : Window(title), closing_(false) { + menu_ = nullptr; +} Win32Window::~Win32Window() {} @@ -57,7 +59,7 @@ bool Win32Window::Create() { AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); // Create window. - hwnd_ = CreateWindowEx(window_ex_style, L"XeniaWindowClass", L"Xenia", + hwnd_ = CreateWindowEx(window_ex_style, L"XeniaWindowClass", title_.c_str(), window_style, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, nullptr, nullptr, hInstance, this); if (!hwnd_) { @@ -65,10 +67,6 @@ bool Win32Window::Create() { return false; } - main_menu_ = CreateMenu(); - AppendMenu(main_menu_, MF_STRING, 0, L"TODO"); - SetMenu(hwnd_, main_menu_); - // Disable flicks. ATOM atom = GlobalAddAtom(L"MicrosoftTabletPenServiceProperty"); const DWORD_PTR dwHwndTabletProperty = @@ -137,7 +135,7 @@ bool Win32Window::set_title(const std::wstring& title) { void Win32Window::Resize(int32_t width, int32_t height) { RECT rc = {0, 0, width, height}; - bool has_menu = main_menu_ ? true : false; + bool has_menu = menu_ ? true : false; AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, has_menu); if (true) { rc.right += 100 - rc.left; @@ -151,7 +149,7 @@ void Win32Window::Resize(int32_t width, int32_t height) { void Win32Window::Resize(int32_t left, int32_t top, int32_t right, int32_t bottom) { RECT rc = {left, top, right, bottom}; - bool has_menu = main_menu_ ? true : false; + bool has_menu = menu_ ? true : false; AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, has_menu); Window::Resize(rc.left, rc.top, rc.right, rc.bottom); } @@ -168,6 +166,13 @@ void Win32Window::OnClose() { } } +void Win32Window::OnSetMenu(MenuItem* menu) { + Win32MenuItem* win_menu = reinterpret_cast(menu); + if (win_menu) { + ::SetMenu(hwnd_, win_menu->handle()); + } +} + LRESULT Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { @@ -197,9 +202,9 @@ LRESULT Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, TABLET_DISABLE_TOUCHSWITCH | TABLET_DISABLE_SMOOTHSCROLLING | TABLET_DISABLE_TOUCHUIFORCEON | TABLET_ENABLE_MULTITOUCHDATA; - case WM_COMMAND: - switch (LOWORD(wParam)) { - // TODO(benvanik): dispatch to menu. + case WM_COMMAND: { + OnCommand(LOWORD(wParam)); + break; } break; } diff --git a/src/xenia/ui/win32/win32_window.h b/src/xenia/ui/win32/win32_window.h index af6d26965..4e1f6a7dd 100644 --- a/src/xenia/ui/win32/win32_window.h +++ b/src/xenia/ui/win32/win32_window.h @@ -13,6 +13,7 @@ #include #include "xenia/ui/win32/win32_control.h" +#include "xenia/ui/win32/win32_menu_item.h" #include "xenia/ui/window.h" namespace xe { @@ -38,13 +39,14 @@ class Win32Window : public Window { bool Create() override; void OnClose() override; + void OnSetMenu(MenuItem*) override; + LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) override; private: void EnableMMCSS(); - HMENU main_menu_; bool closing_; }; diff --git a/src/xenia/ui/window.h b/src/xenia/ui/window.h index 311c60319..11618a4ed 100644 --- a/src/xenia/ui/window.h +++ b/src/xenia/ui/window.h @@ -15,6 +15,7 @@ #include "xenia/base/delegate.h" #include "xenia/ui/control.h" #include "xenia/ui/ui_event.h" +#include "xenia/ui/menu_item.h" namespace xe { namespace ui { @@ -45,6 +46,12 @@ class Window : public T { on_closed(e); } + virtual void SetMenu(MenuItem* menu) { + menu_ = menu; + + OnSetMenu(menu); + } + public: Delegate on_shown; Delegate on_hidden; @@ -74,7 +81,11 @@ class Window : public T { virtual void OnClose() {} - private: + virtual void OnSetMenu(MenuItem* menu) {} + + virtual void OnCommand(int id) {} + + MenuItem* menu_; std::wstring title_; };