
This is really cool! :^) Apps currently refuse to start if the WindowServer isn't listening on the socket in /wsportal. This makes sense, but I guess it would also be nice to have some sort of "wait for server on startup" mode. This has performance issues, and I'll work on those, but this stuff seems to actually work and I'm very happy with that.
163 lines
4.1 KiB
C++
163 lines
4.1 KiB
C++
#include "WSMenu.h"
|
|
#include "WSMenuItem.h"
|
|
#include "WSWindow.h"
|
|
#include "WSMessage.h"
|
|
#include "WSMessageLoop.h"
|
|
#include "WSWindowManager.h"
|
|
#include <WindowServer/WSClientConnection.h>
|
|
#include <SharedGraphics/Painter.h>
|
|
#include <SharedGraphics/Font.h>
|
|
|
|
WSMenu::WSMenu(int client_id, int menu_id, String&& name)
|
|
: m_client_id(client_id)
|
|
, m_menu_id(menu_id)
|
|
, m_name(move(name))
|
|
{
|
|
}
|
|
|
|
WSMenu::~WSMenu()
|
|
{
|
|
}
|
|
|
|
void WSMenu::set_menu_window(OwnPtr<WSWindow>&& menu_window)
|
|
{
|
|
m_menu_window = move(menu_window);
|
|
}
|
|
|
|
const Font& WSMenu::font() const
|
|
{
|
|
return Font::default_font();
|
|
}
|
|
|
|
int WSMenu::width() const
|
|
{
|
|
int longest = 0;
|
|
for (auto& item : m_items) {
|
|
if (item->type() == WSMenuItem::Text)
|
|
longest = max(longest, font().width(item->text()));
|
|
}
|
|
|
|
return max(longest, rect_in_menubar().width()) + horizontal_padding();
|
|
}
|
|
|
|
int WSMenu::height() const
|
|
{
|
|
if (m_items.is_empty())
|
|
return 0;
|
|
return (m_items.last()->rect().bottom() - 1) + vertical_padding();
|
|
}
|
|
|
|
void WSMenu::redraw()
|
|
{
|
|
ASSERT(menu_window());
|
|
draw();
|
|
menu_window()->invalidate();
|
|
}
|
|
|
|
WSWindow& WSMenu::ensure_menu_window()
|
|
{
|
|
if (!m_menu_window) {
|
|
Point next_item_location(1, vertical_padding() / 2);
|
|
for (auto& item : m_items) {
|
|
int height = 0;
|
|
if (item->type() == WSMenuItem::Text)
|
|
height = item_height();
|
|
else if (item->type() == WSMenuItem::Separator)
|
|
height = 7;
|
|
item->set_rect({ next_item_location, { width() - 2, height } });
|
|
next_item_location.move_by(0, height);
|
|
}
|
|
|
|
auto window = make<WSWindow>(*this);
|
|
window->set_rect(0, 0, width(), height());
|
|
m_menu_window = move(window);
|
|
draw();
|
|
}
|
|
return *m_menu_window;
|
|
}
|
|
|
|
void WSMenu::draw()
|
|
{
|
|
ASSERT(menu_window());
|
|
ASSERT(menu_window()->backing());
|
|
Painter painter(*menu_window()->backing());
|
|
|
|
Rect rect { { }, menu_window()->size() };
|
|
painter.draw_rect(rect, Color::White);
|
|
painter.fill_rect(rect.shrunken(2, 2), Color::LightGray);
|
|
|
|
for (auto& item : m_items) {
|
|
if (item->type() == WSMenuItem::Text) {
|
|
Color text_color = Color::Black;
|
|
if (item.ptr() == m_hovered_item) {
|
|
painter.fill_rect(item->rect(), WSWindowManager::the().menu_selection_color());
|
|
text_color = Color::White;
|
|
}
|
|
painter.draw_text(item->rect().translated(left_padding(), 0), item->text(), TextAlignment::CenterLeft, text_color);
|
|
} else if (item->type() == WSMenuItem::Separator) {
|
|
Point p1(1, item->rect().center().y());
|
|
Point p2(width() - 2, item->rect().center().y());
|
|
painter.draw_line(p1, p2, Color::MidGray);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WSMenu::on_window_message(WSMessage& message)
|
|
{
|
|
ASSERT(menu_window());
|
|
if (message.type() == WSMessage::MouseMove) {
|
|
auto* item = item_at(static_cast<WSMouseEvent&>(message).position());
|
|
if (!item || m_hovered_item == item)
|
|
return;
|
|
m_hovered_item = item;
|
|
redraw();
|
|
return;
|
|
}
|
|
|
|
if (message.type() == WSMessage::MouseUp) {
|
|
if (!m_hovered_item)
|
|
return;
|
|
did_activate(*m_hovered_item);
|
|
clear_hovered_item();
|
|
return;
|
|
}
|
|
}
|
|
|
|
void WSMenu::clear_hovered_item()
|
|
{
|
|
if (!m_hovered_item)
|
|
return;
|
|
m_hovered_item = nullptr;
|
|
redraw();
|
|
}
|
|
|
|
void WSMenu::did_activate(WSMenuItem& item)
|
|
{
|
|
if (on_item_activation)
|
|
on_item_activation(item);
|
|
|
|
close();
|
|
|
|
GUI_ServerMessage message;
|
|
message.type = GUI_ServerMessage::Type::MenuItemActivated;
|
|
message.menu.menu_id = m_menu_id;
|
|
message.menu.identifier = item.identifier();
|
|
|
|
if (auto* client = WSClientConnection::from_client_id(m_client_id))
|
|
client->post_message(message);
|
|
}
|
|
|
|
WSMenuItem* WSMenu::item_at(const Point& position)
|
|
{
|
|
for (auto& item : m_items) {
|
|
if (!item->rect().contains(position))
|
|
continue;
|
|
return item.ptr();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void WSMenu::close()
|
|
{
|
|
WSWindowManager::the().close_menu(*this);
|
|
};
|