mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 13:57:35 +00:00
WindowServer: Organize system menu app shortcuts into categories
If the .af file for an app contains the App/Category key, we'll now put it in a submenu of the system menu, together with all the other apps in that same category. This is pretty neat! :^)
This commit is contained in:
parent
3a71c018bf
commit
74be54cce8
5 changed files with 77 additions and 20 deletions
|
@ -198,6 +198,17 @@ void WSMenu::draw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void close_everyone_not_in_lineage(WSMenu& menu)
|
||||||
|
{
|
||||||
|
for (auto& open_menu : WSWindowManager::the().menu_manager().open_menu_stack()) {
|
||||||
|
if (!open_menu)
|
||||||
|
continue;
|
||||||
|
if (&menu == open_menu.ptr() || open_menu->is_menu_ancestor_of(menu))
|
||||||
|
continue;
|
||||||
|
open_menu->menu_window()->set_visible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WSMenu::event(CEvent& event)
|
void WSMenu::event(CEvent& event)
|
||||||
{
|
{
|
||||||
if (event.type() == WSEvent::MouseMove) {
|
if (event.type() == WSEvent::MouseMove) {
|
||||||
|
@ -207,21 +218,10 @@ void WSMenu::event(CEvent& event)
|
||||||
return;
|
return;
|
||||||
m_hovered_item = item;
|
m_hovered_item = item;
|
||||||
if (m_hovered_item->is_submenu()) {
|
if (m_hovered_item->is_submenu()) {
|
||||||
|
close_everyone_not_in_lineage(*m_hovered_item->submenu());
|
||||||
m_hovered_item->submenu()->popup(m_hovered_item->rect().top_right().translated(menu_window()->rect().location()), true);
|
m_hovered_item->submenu()->popup(m_hovered_item->rect().top_right().translated(menu_window()->rect().location()), true);
|
||||||
} else {
|
} else {
|
||||||
bool close_remaining_menus = false;
|
close_everyone_not_in_lineage(*this);
|
||||||
for (auto& open_menu : WSWindowManager::the().menu_manager().open_menu_stack()) {
|
|
||||||
if (!open_menu)
|
|
||||||
continue;
|
|
||||||
if (close_remaining_menus) {
|
|
||||||
open_menu->menu_window()->set_visible(false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (open_menu == this) {
|
|
||||||
close_remaining_menus = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
redraw();
|
redraw();
|
||||||
return;
|
return;
|
||||||
|
@ -307,3 +307,17 @@ void WSMenu::popup(const Point& position, bool is_submenu)
|
||||||
window.set_visible(true);
|
window.set_visible(true);
|
||||||
WSWindowManager::the().set_current_menu(this, is_submenu);
|
WSWindowManager::the().set_current_menu(this, is_submenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WSMenu::is_menu_ancestor_of(const WSMenu& other) const
|
||||||
|
{
|
||||||
|
for (auto& item : m_items) {
|
||||||
|
if (!item.is_submenu())
|
||||||
|
continue;
|
||||||
|
auto& submenu = *const_cast<WSMenuItem&>(item).submenu();
|
||||||
|
if (&submenu == &other)
|
||||||
|
return true;
|
||||||
|
if (submenu.is_menu_ancestor_of(other))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -75,6 +75,8 @@ public:
|
||||||
|
|
||||||
void popup(const Point&, bool is_submenu = false);
|
void popup(const Point&, bool is_submenu = false);
|
||||||
|
|
||||||
|
bool is_menu_ancestor_of(const WSMenu&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void event(CEvent&) override;
|
virtual void event(CEvent&) override;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "WSMenuItem.h"
|
#include "WSMenuItem.h"
|
||||||
#include "WSClientConnection.h"
|
#include "WSClientConnection.h"
|
||||||
#include "WSMenu.h"
|
#include "WSMenu.h"
|
||||||
|
#include "WSWindowManager.h"
|
||||||
#include <LibDraw/GraphicsBitmap.h>
|
#include <LibDraw/GraphicsBitmap.h>
|
||||||
|
|
||||||
WSMenuItem::WSMenuItem(WSMenu& menu, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked, const GraphicsBitmap* icon)
|
WSMenuItem::WSMenuItem(WSMenu& menu, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked, const GraphicsBitmap* icon)
|
||||||
|
@ -45,6 +46,7 @@ void WSMenuItem::set_checked(bool checked)
|
||||||
WSMenu* WSMenuItem::submenu()
|
WSMenu* WSMenuItem::submenu()
|
||||||
{
|
{
|
||||||
ASSERT(is_submenu());
|
ASSERT(is_submenu());
|
||||||
ASSERT(m_menu.client());
|
if (m_menu.client())
|
||||||
return m_menu.client()->find_menu_by_id(m_submenu_id);
|
return m_menu.client()->find_menu_by_id(m_submenu_id);
|
||||||
|
return WSWindowManager::the().find_internal_menu_by_id(m_submenu_id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#include <AK/LogStream.h>
|
#include <AK/LogStream.h>
|
||||||
#include <AK/StdLibExtras.h>
|
#include <AK/StdLibExtras.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibCore/CTimer.h>
|
|
||||||
#include <LibCore/CDirIterator.h>
|
#include <LibCore/CDirIterator.h>
|
||||||
|
#include <LibCore/CTimer.h>
|
||||||
#include <LibDraw/CharacterBitmap.h>
|
#include <LibDraw/CharacterBitmap.h>
|
||||||
#include <LibDraw/Font.h>
|
#include <LibDraw/Font.h>
|
||||||
#include <LibDraw/PNGLoader.h>
|
#include <LibDraw/PNGLoader.h>
|
||||||
|
@ -49,6 +49,7 @@ WSWindowManager::WSWindowManager()
|
||||||
String binary_name;
|
String binary_name;
|
||||||
String description;
|
String description;
|
||||||
String icon_path;
|
String icon_path;
|
||||||
|
String category;
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector<AppMenuItem> apps;
|
Vector<AppMenuItem> apps;
|
||||||
|
@ -62,16 +63,41 @@ WSWindowManager::WSWindowManager()
|
||||||
continue;
|
continue;
|
||||||
auto app_name = af->read_entry("App", "Name");
|
auto app_name = af->read_entry("App", "Name");
|
||||||
auto app_executable = af->read_entry("App", "Executable");
|
auto app_executable = af->read_entry("App", "Executable");
|
||||||
|
auto app_category = af->read_entry("App", "Category");
|
||||||
auto app_icon_path = af->read_entry("Icons", "16x16");
|
auto app_icon_path = af->read_entry("Icons", "16x16");
|
||||||
apps.append({ app_executable, app_name, app_icon_path });
|
apps.append({ app_executable, app_name, app_icon_path, app_category });
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 system_menu_name[] = { 0xc3, 0xb8, 0 };
|
u8 system_menu_name[] = { 0xc3, 0xb8, 0 };
|
||||||
m_system_menu = WSMenu::construct(nullptr, -1, String((const char*)system_menu_name));
|
m_system_menu = WSMenu::construct(nullptr, -1, String((const char*)system_menu_name));
|
||||||
|
|
||||||
int appIndex = 1;
|
// First we construct all the necessary app category submenus.
|
||||||
for (const auto& app : apps) {
|
for (const auto& app : apps) {
|
||||||
m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, appIndex++, app.description, String(), true, false, false, load_png(app.icon_path)));
|
if (app.category.is_null())
|
||||||
|
continue;
|
||||||
|
if (m_app_category_menus.contains(app.category))
|
||||||
|
continue;
|
||||||
|
auto category_menu = WSMenu::construct(nullptr, 5000 + m_app_category_menus.size(), app.category);
|
||||||
|
category_menu->on_item_activation = [apps](auto& item) {
|
||||||
|
if (item.identifier() >= 1 && item.identifier() <= 1u + apps.size() - 1) {
|
||||||
|
if (fork() == 0) {
|
||||||
|
const auto& bin = apps[item.identifier() - 1].binary_name;
|
||||||
|
execl(bin.characters(), bin.characters(), nullptr);
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto item = make<WSMenuItem>(*m_system_menu, -1, app.category);
|
||||||
|
item->set_submenu_id(category_menu->menu_id());
|
||||||
|
m_system_menu->add_item(move(item));
|
||||||
|
m_app_category_menus.set(app.category, move(category_menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then we create and insert all the app menu items into the right place.
|
||||||
|
int app_identifier = 1;
|
||||||
|
for (const auto& app : apps) {
|
||||||
|
auto parent_menu = m_app_category_menus.get(app.category).value_or(*m_system_menu);
|
||||||
|
parent_menu->add_item(make<WSMenuItem>(*m_system_menu, app_identifier++, app.description, String(), true, false, false, load_png(app.icon_path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, WSMenuItem::Separator));
|
m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, WSMenuItem::Separator));
|
||||||
|
@ -494,7 +520,7 @@ bool WSWindowManager::process_ongoing_window_drag(WSMouseEvent& event, WSWindow*
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (event.type() == WSEvent::MouseMove) {
|
if (event.type() == WSEvent::MouseMove) {
|
||||||
|
|
||||||
#ifdef DRAG_DEBUG
|
#ifdef DRAG_DEBUG
|
||||||
dbg() << "[WM] Dragging, origin: " << m_drag_origin << ", now: " << event.position();
|
dbg() << "[WM] Dragging, origin: " << m_drag_origin << ", now: " << event.position();
|
||||||
if (m_drag_window->is_maximized()) {
|
if (m_drag_window->is_maximized()) {
|
||||||
|
@ -1121,3 +1147,12 @@ Rect WSWindowManager::maximized_window_rect(const WSWindow& window) const
|
||||||
|
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WSMenu* WSWindowManager::find_internal_menu_by_id(int menu_id)
|
||||||
|
{
|
||||||
|
for (auto& it : m_app_category_menus) {
|
||||||
|
if (menu_id == it.value->menu_id())
|
||||||
|
return it.value;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
|
@ -148,6 +148,8 @@ public:
|
||||||
m_current_menubar->for_each_menu(callback);
|
m_current_menubar->for_each_menu(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WSMenu* find_internal_menu_by_id(int);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NonnullRefPtr<WSCursor> get_cursor(const String& name);
|
NonnullRefPtr<WSCursor> get_cursor(const String& name);
|
||||||
NonnullRefPtr<WSCursor> get_cursor(const String& name, const Point& hotspot);
|
NonnullRefPtr<WSCursor> get_cursor(const String& name, const Point& hotspot);
|
||||||
|
@ -262,6 +264,8 @@ private:
|
||||||
WeakPtr<WSButton> m_hovered_button;
|
WeakPtr<WSButton> m_hovered_button;
|
||||||
|
|
||||||
RefPtr<CConfigFile> m_wm_config;
|
RefPtr<CConfigFile> m_wm_config;
|
||||||
|
|
||||||
|
HashMap<String, NonnullRefPtr<WSMenu>> m_app_category_menus;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Callback>
|
template<typename Callback>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue