This commit is contained in:
Adrian 2025-01-17 10:36:52 +01:00 committed by GitHub
commit f7c9d3f7e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 253 additions and 32 deletions

View File

@ -60,6 +60,8 @@ DECLARE_bool(clear_memory_page_state);
DECLARE_bool(d3d12_readback_resolve);
DECLARE_int32(keyboard_mode);
DEFINE_bool(fullscreen, false, "Whether to launch the emulator in fullscreen.",
"Display");
@ -576,6 +578,7 @@ bool EmulatorWindow::Initialize() {
// FIXME: This code is really messy.
auto main_menu = MenuItem::Create(MenuItem::Type::kNormal);
auto file_menu = MenuItem::Create(MenuItem::Type::kPopup, "&File");
auto testing_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Testing");
auto recent_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Open Recent");
auto zar_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Zar Package");
FillRecentlyLaunchedTitlesMenu(recent_menu.get());
@ -612,6 +615,17 @@ bool EmulatorWindow::Initialize() {
}
main_menu->AddChild(std::move(file_menu));
testing_menu->AddChild(
std::move(MenuItem::Create(MenuItem::Type::kChecked, "&SubMenu 1")));
testing_menu->AddChild(
std::move(MenuItem::Create(MenuItem::Type::kChecked, "&SubMenu 2")));
testing_menu->AddChild(
std::move(MenuItem::Create(MenuItem::Type::kChecked, "&SubMenu 3")));
testing_menu->AddChild(MenuItem::Create(MenuItem::Type::kSeparator));
testing_menu->AddChild(
std::move(MenuItem::Create(MenuItem::Type::kChecked, "&SubMenu 4")));
main_menu->AddChild(std::move(testing_menu));
// Profile Menu
auto profile_menu = MenuItem::Create(MenuItem::Type::kPopup, "&Profile");
{
@ -727,6 +741,14 @@ bool EmulatorWindow::Initialize() {
}
main_menu->AddChild(std::move(help_menu));
// if (cvars::keyboard_mode == 2) {
auto toggle_menu =
MenuItem::Create(MenuItem::Type::kString, "&Disable Toolbar",
std::bind(&EmulatorWindow::ToggleToolBar, this));
main_menu->AddChild(std::move(toggle_menu));
//}
window_->SetMainMenu(std::move(main_menu));
window_->SetMainMenuEnabled(false);
@ -830,11 +852,30 @@ void EmulatorWindow::ApplyDisplayConfigForCvars() {
}
}
void EmulatorWindow::ToggleToolBar() {
const uint32_t toolbar_pos = 8;
auto toolbar_item = window_->GetMainMenu()->GetItem(toolbar_pos);
window_->SetMainMenuEnabled(!window_->GetMainMenuEnabled());
toolbar_item->SetEnabled(true);
if (window_->GetMainMenuEnabled()) {
toolbar_item->ModifyString("Disable Toolbar");
} else {
toolbar_item->ModifyString("Enable Toolbar");
}
}
void EmulatorWindow::OnKeyDown(ui::KeyEvent& e) {
if (!emulator_initialized_) {
return;
}
if (!window_->GetMainMenuEnabled()) {
return;
}
switch (e.virtual_key()) {
case ui::VirtualKey::kO: {
if (!e.is_ctrl_pressed()) {

View File

@ -207,6 +207,8 @@ class EmulatorWindow {
GetGuestOutputPaintConfigForCvars();
void ApplyDisplayConfigForCvars();
void ToggleToolBar();
void OnKeyDown(ui::KeyEvent& e);
void OnMouseDown(const ui::MouseEvent& e);
void ToggleFullscreenOnDoubleClick();

View File

@ -29,6 +29,8 @@ MenuItem::MenuItem(Type type, const std::string& text,
const std::string& hotkey, std::function<void()> callback)
: type_(type),
parent_item_(nullptr),
previous_item_(nullptr),
next_item_(nullptr),
text_(text),
hotkey_(hotkey),
callback_(std::move(callback)) {}
@ -46,6 +48,14 @@ void MenuItem::AddChild(std::unique_ptr<MenuItem> child_item) {
void MenuItem::AddChild(MenuItemPtr child_item) {
auto child_item_ptr = child_item.get();
child_item_ptr->parent_item_ = this;
// Doubly Linked List
if (children_.size()) {
child_item_ptr->previous_item_ = children_.back().get();
child_item_ptr->previous_item_->next_item_ = child_item_ptr;
}
children_.emplace_back(std::move(child_item));
OnChildAdded(child_item_ptr);
}
@ -60,9 +70,20 @@ void MenuItem::RemoveChild(MenuItem* child_item) {
}
}
MenuItem* MenuItem::child(size_t index) { return children_[index].get(); }
MenuItem* MenuItem::GetItem(uint32_t index) { return children_[index].get(); }
void MenuItem::SetPreviousItem(MenuItem* previous_item) {
previous_item_ = previous_item;
}
void MenuItem::SetNextItem(MenuItem* next_item) { next_item_ = next_item; }
void MenuItem::OnSelected() {
if (type() == Type::kChecked) {
ResetChecked();
SetChecked(true);
}
if (callback_) {
callback_();
// Note that this MenuItem might have been destroyed by the callback.

View File

@ -28,9 +28,10 @@ class MenuItem {
enum class Type {
kPopup, // Popup menu (submenu)
kSeparator,
kSeparator, // Seperator between elements
kNormal, // Root menu
kString, // Menu is just a string
kChecked // Menu is child of submenu with checkmarks
};
static std::unique_ptr<MenuItem> Create(Type type);
@ -43,8 +44,11 @@ class MenuItem {
virtual ~MenuItem();
MenuItem* parent_item() const { return parent_item_; }
Type type() { return type_; }
MenuItem* GetParentItem() const { return parent_item_; }
MenuItem* GetPreviousItem() const { return previous_item_; }
MenuItem* GetNextItem() const { return next_item_; }
Type type() const { return type_; }
const std::string& text() { return text_; }
const std::string& hotkey() { return hotkey_; }
@ -57,9 +61,17 @@ class MenuItem {
void AddChild(std::unique_ptr<MenuItem> child_item);
void AddChild(MenuItemPtr child_item);
void RemoveChild(MenuItem* child_item);
MenuItem* child(size_t index);
void SetPreviousItem(MenuItem* previous_item);
void SetNextItem(MenuItem* next_item);
MenuItem* GetItem(uint32_t index);
virtual void SetEnabledCascade(bool enabled) {}
virtual void SetEnabled(bool enabled) {}
virtual void SetEnabled(uint32_t position, bool enabled) {}
virtual void SetChecked(bool checked) {}
virtual void SetChecked(uint32_t identifier, bool checked) {}
virtual void ResetChecked() {};
virtual void ModifyString(std::string modify_str) {}
protected:
MenuItem(Type type, const std::string& text, const std::string& hotkey,
@ -74,6 +86,8 @@ class MenuItem {
Type type_;
MenuItem* parent_item_;
MenuItem* previous_item_;
MenuItem* next_item_;
std::vector<MenuItemPtr> children_;
std::string text_;
std::string hotkey_;

View File

@ -317,7 +317,8 @@ void Window::SetMainMenuEnabled(bool enabled) {
// pressing) that may execute callbacks potentially destroying the Window via
// the outer architecture.
WindowDestructionReceiver destruction_receiver(this);
main_menu_->SetEnabled(enabled);
main_menu_->SetEnabledCascade(enabled);
main_menu_enabled_ = enabled;
if (destruction_receiver.IsWindowDestroyed()) {
return;
}

View File

@ -349,6 +349,10 @@ class Window {
}
}
MenuItem* GetMainMenu() const { return main_menu_.get(); }
bool GetMainMenuEnabled() const { return main_menu_enabled_; }
protected:
// The receiver, which must never be instantiated in the Window object itself
// (rather, usually it should be created as a local variable, because only
@ -509,7 +513,6 @@ class Window {
// default one. Returns whether the icon has been updated successfully.
virtual void LoadAndApplyIcon(const void* buffer, size_t size,
bool can_apply_state_in_current_phase) {}
MenuItem* GetMainMenu() const { return main_menu_.get(); }
// May be called to add, replace or remove the main menu.
virtual void ApplyNewMainMenu(MenuItem* old_main_menu) {}
// If there's main menu, and state can be applied, will be called to make the
@ -711,6 +714,8 @@ class Window {
bool has_focus_ = false;
bool main_menu_enabled_ = false;
Presenter* presenter_ = nullptr;
std::unique_ptr<Surface> presenter_surface_;
// Whether currently in InPaint to prevent recursive painting in case it's

View File

@ -128,13 +128,14 @@ bool Win32Window::OpenImpl() {
// Create the window. Though WM_NCCREATE will assign to `hwnd_` too, still do
// the assignment here to handle the case of a failure after WM_NCCREATE, for
// instance.
hwnd_ = CreateWindowExW(
window_ex_style, L"XeniaWindowClass",
reinterpret_cast<LPCWSTR>(xe::to_utf16(GetTitle()).c_str()), window_style,
CW_USEDEFAULT, CW_USEDEFAULT,
const auto window_title = xe::to_utf16(GetTitle());
hwnd_ = CreateWindowExW(window_ex_style, L"XeniaWindowClass",
reinterpret_cast<LPCWSTR>(window_title.c_str()),
window_style, CW_USEDEFAULT, CW_USEDEFAULT,
window_size_rect.right - window_size_rect.left,
window_size_rect.bottom - window_size_rect.top, nullptr, nullptr,
hinstance, this);
window_size_rect.bottom - window_size_rect.top,
nullptr, nullptr, hinstance, this);
if (!hwnd_) {
XELOGE("CreateWindowExW failed");
return false;
@ -1202,16 +1203,16 @@ LRESULT Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam,
TABLET_ENABLE_MULTITOUCHDATA;
case WM_MENUCOMMAND: {
MENUINFO menu_info = {0};
MENUINFO menu_info = {};
menu_info.cbSize = sizeof(menu_info);
menu_info.fMask = MIM_MENUDATA;
GetMenuInfo(HMENU(lParam), &menu_info);
auto parent_item = reinterpret_cast<Win32MenuItem*>(menu_info.dwMenuData);
auto child_item =
reinterpret_cast<Win32MenuItem*>(parent_item->child(wParam));
assert_not_null(child_item);
auto next_item = reinterpret_cast<Win32MenuItem*>(
parent_item->GetItem(static_cast<uint32_t>(wParam)));
assert_not_null(next_item);
WindowDestructionReceiver destruction_receiver(this);
child_item->OnSelected();
next_item->OnSelected();
if (destruction_receiver.IsWindowDestroyed()) {
break;
}
@ -1286,19 +1287,25 @@ Win32MenuItem::Win32MenuItem(Type type, const std::string& text,
switch (type) {
case MenuItem::Type::kNormal:
handle_ = CreateMenu();
identifier_ = -1;
break;
case MenuItem::Type::kPopup:
handle_ = CreatePopupMenu();
identifier_ = -1;
break;
case MenuItem::Type::kSeparator:
identifier_ = -1;
break;
default:
// May just be a placeholder.
break;
}
if (handle_) {
MENUINFO menu_info = {0};
MENUINFO menu_info = {};
menu_info.cbSize = sizeof(menu_info);
menu_info.fMask = MIM_MENUDATA | MIM_STYLE;
menu_info.dwMenuData = ULONG_PTR(this);
menu_info.dwMenuData = reinterpret_cast<ULONG_PTR>(this);
menu_info.dwStyle = MNS_NOTIFYBYPOS;
SetMenuInfo(handle_, &menu_info);
}
@ -1310,16 +1317,87 @@ Win32MenuItem::~Win32MenuItem() {
}
}
void Win32MenuItem::SetEnabledCascade(bool enabled) {
for (const auto& item : children_) {
item->SetEnabled(enabled);
}
}
void Win32MenuItem::SetEnabled(bool enabled) {
SetEnabled(position(), enabled);
}
void Win32MenuItem::SetEnabled(uint32_t position, bool enabled) {
UINT enable_flags = MF_BYPOSITION | (enabled ? MF_ENABLED : MF_GRAYED);
UINT i = 0;
for (auto iter = children_.begin(); iter != children_.end(); ++iter, ++i) {
EnableMenuItem(handle_, i, enable_flags);
const auto parent_item = static_cast<Win32MenuItem*>(GetParentItem());
if (parent_item) {
EnableMenuItem(parent_item->handle(), position, enable_flags);
} else if (handle_) {
EnableMenuItem(handle_, position, enable_flags);
}
}
void Win32MenuItem::SetChecked(bool checked) {
SetChecked(identifier(), checked);
}
void Win32MenuItem::SetChecked(uint32_t identifier, bool checked) {
MENUITEMINFOW MII = {};
MII.cbSize = sizeof(MENUITEMINFO);
MII.fMask = MIIM_STATE;
MII.fState = checked ? MFS_CHECKED : MFS_UNCHECKED;
// assert_true(handle_ != 0);
if (type() == Type::kChecked) {
const auto parent_item = static_cast<Win32MenuItem*>(GetParentItem());
if (parent_item) {
SetMenuItemInfoW(parent_item->handle(), identifier, false, &MII);
}
}
}
void Win32MenuItem::ResetChecked() {
if (type() == Type::kChecked) {
const auto parent = static_cast<Win32MenuItem*>(GetParentItem());
if (parent) {
for (const auto& item : parent->children_) {
item->SetChecked(false);
}
}
}
}
void Win32MenuItem::ModifyString(std::string modify_str) {
MENUITEMINFOW MII = {};
MII.cbSize = sizeof(MENUITEMINFO);
MII.fMask = MIIM_STRING;
const auto updated_string = xe::to_utf16(modify_str);
const auto updated_text = reinterpret_cast<LPCWSTR>(updated_string.c_str());
MII.dwTypeData = const_cast<LPWSTR>(updated_text);
// assert_true(handle_ != 0);
if (type() == Type::kString) {
const auto parent_item = static_cast<Win32MenuItem*>(GetParentItem());
if (parent_item) {
SetMenuItemInfoW(parent_item->handle(), position(), true, &MII);
}
}
}
void Win32MenuItem::OnChildAdded(MenuItem* generic_child_item) {
auto child_item = static_cast<Win32MenuItem*>(generic_child_item);
auto parent_item = static_cast<Win32MenuItem*>(child_item->GetParentItem());
child_item->position_ =
static_cast<uint32_t>(parent_item->children_.size()) - 1;
switch (child_item->type()) {
case MenuItem::Type::kNormal:
@ -1331,20 +1409,55 @@ void Win32MenuItem::OnChildAdded(MenuItem* generic_child_item) {
reinterpret_cast<LPCWSTR>(xe::to_utf16(child_item->text()).c_str()));
break;
case MenuItem::Type::kSeparator:
AppendMenuW(handle_, MF_SEPARATOR, UINT_PTR(child_item->handle_), 0);
AppendMenuW(handle_, MF_SEPARATOR, 0, nullptr);
break;
case MenuItem::Type::kChecked:
case MenuItem::Type::kString:
auto full_name = child_item->text();
child_item->identifier_ = parent_item->next_identifier();
std::string full_name = child_item->text();
if (!child_item->hotkey().empty()) {
full_name += "\t" + child_item->hotkey();
full_name = fmt::format("{}\t{}", full_name, child_item->hotkey());
}
AppendMenuW(handle_, MF_STRING, UINT_PTR(child_item->handle_),
AppendMenuW(handle_, MF_STRING, child_item->identifier(),
reinterpret_cast<LPCWSTR>(xe::to_utf16(full_name).c_str()));
break;
}
}
void Win32MenuItem::OnChildRemoved(MenuItem* generic_child_item) {}
void Win32MenuItem::OnChildRemoved(MenuItem* generic_child_item) {
auto child_item = static_cast<Win32MenuItem*>(generic_child_item);
auto previous_item =
static_cast<Win32MenuItem*>(child_item->GetPreviousItem());
auto next_item = static_cast<Win32MenuItem*>(child_item->GetNextItem());
auto parent_item = static_cast<Win32MenuItem*>(child_item->GetParentItem());
UINT flags = MF_BYPOSITION;
const uint32_t position = child_item->position();
bool deleted_item = false;
if (parent_item) {
deleted_item = DeleteMenu(parent_item->handle(), position, flags);
} else if (handle_) {
deleted_item = DeleteMenu(handle_, position, flags);
}
// TODO: Reindex linked list positions
// Update Linked List
if (deleted_item) {
if (previous_item) {
previous_item->SetNextItem(child_item->GetNextItem());
}
if (next_item) {
next_item->SetPreviousItem(child_item->GetPreviousItem());
}
}
}
} // namespace ui
} // namespace xe

View File

@ -159,8 +159,30 @@ class Win32MenuItem : public MenuItem {
~Win32MenuItem() override;
HMENU handle() const { return handle_; }
uint32_t identifier() const { return identifier_; }
uint32_t position() const { return position_; }
uint32_t next_identifier() const {
if (children_.size() > 1) {
auto item = static_cast<Win32MenuItem*>(
children_.back().get()->GetPreviousItem());
while (item != nullptr && item->identifier() == -1) {
item = static_cast<Win32MenuItem*>(item->GetPreviousItem());
}
return item == nullptr ? 0 : item->identifier() + 1;
} else {
return 0;
}
}
void SetEnabledCascade(bool enabled) override;
void SetEnabled(bool enabled) override;
void SetEnabled(uint32_t position, bool enabled) override;
void SetChecked(bool checked) override;
void SetChecked(uint32_t identifier, bool checked) override;
void ResetChecked() override;
void ModifyString(std::string modify_str) override;
using MenuItem::OnSelected;
@ -170,6 +192,8 @@ class Win32MenuItem : public MenuItem {
private:
HMENU handle_ = nullptr;
uint32_t identifier_ = 0;
uint32_t position_ = 0;
};
} // namespace ui