mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 08:58:11 +00:00
WindowServer: Start implementing a menu system.
I'm going with a global top-of-the-screen menu instead of per-window menus. The basic idea is that menus will live in the WindowServer and clients can create menus via WindowServer requests.
This commit is contained in:
parent
7abef6ba9e
commit
443b043b49
14 changed files with 487 additions and 6 deletions
|
@ -63,6 +63,9 @@ WINDOWSERVER_OBJS = \
|
||||||
../WindowServer/WSWindow.o \
|
../WindowServer/WSWindow.o \
|
||||||
../WindowServer/WSWindowManager.o \
|
../WindowServer/WSWindowManager.o \
|
||||||
../WindowServer/WSScreen.o \
|
../WindowServer/WSScreen.o \
|
||||||
|
../WindowServer/WSMenuBar.o \
|
||||||
|
../WindowServer/WSMenu.o \
|
||||||
|
../WindowServer/WSMenuItem.o \
|
||||||
../WindowServer/main.o
|
../WindowServer/main.o
|
||||||
|
|
||||||
AK_OBJS = \
|
AK_OBJS = \
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
|
|
||||||
byte glyph_width() const { return m_glyph_width; }
|
byte glyph_width() const { return m_glyph_width; }
|
||||||
byte glyph_height() const { return m_glyph_height; }
|
byte glyph_height() const { return m_glyph_height; }
|
||||||
|
int width(const String& string) const { return string.length() * glyph_width(); }
|
||||||
|
|
||||||
String name() const { return m_name; }
|
String name() const { return m_name; }
|
||||||
void set_name(const String& name) { m_name = name; }
|
void set_name(const String& name) { m_name = name; }
|
||||||
|
|
|
@ -39,6 +39,25 @@ GraphicsBitmap::GraphicsBitmap(Process& process, const Size& size)
|
||||||
|
|
||||||
m_data = (RGBA32*)m_server_region->laddr().as_ptr();
|
m_data = (RGBA32*)m_server_region->laddr().as_ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RetainPtr<GraphicsBitmap> GraphicsBitmap::create_kernel_only(const Size& size)
|
||||||
|
{
|
||||||
|
return adopt(*new GraphicsBitmap(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphicsBitmap::GraphicsBitmap(const Size& size)
|
||||||
|
: m_size(size)
|
||||||
|
, m_pitch(size.width() * sizeof(RGBA32))
|
||||||
|
{
|
||||||
|
InterruptDisabler disabler;
|
||||||
|
size_t size_in_bytes = size.width() * size.height() * sizeof(RGBA32);
|
||||||
|
auto vmo = VMObject::create_anonymous(size_in_bytes);
|
||||||
|
auto& server = WSMessageLoop::the().server_process();
|
||||||
|
m_server_region = server.allocate_region_with_vmo(LinearAddress(), size_in_bytes, move(vmo), 0, "GraphicsBitmap (server)", true, false);
|
||||||
|
m_server_region->set_shared(true);
|
||||||
|
m_server_region->set_is_bitmap(true);
|
||||||
|
m_data = (RGBA32*)m_server_region->laddr().as_ptr();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::create_wrapper(const Size& size, RGBA32* data)
|
RetainPtr<GraphicsBitmap> GraphicsBitmap::create_wrapper(const Size& size, RGBA32* data)
|
||||||
|
|
|
@ -15,6 +15,7 @@ class GraphicsBitmap : public Retainable<GraphicsBitmap> {
|
||||||
public:
|
public:
|
||||||
#ifdef KERNEL
|
#ifdef KERNEL
|
||||||
static RetainPtr<GraphicsBitmap> create(Process&, const Size&);
|
static RetainPtr<GraphicsBitmap> create(Process&, const Size&);
|
||||||
|
static RetainPtr<GraphicsBitmap> create_kernel_only(const Size&);
|
||||||
#endif
|
#endif
|
||||||
static RetainPtr<GraphicsBitmap> create_wrapper(const Size&, RGBA32*);
|
static RetainPtr<GraphicsBitmap> create_wrapper(const Size&, RGBA32*);
|
||||||
static RetainPtr<GraphicsBitmap> load_from_file(const String& path, const Size&);
|
static RetainPtr<GraphicsBitmap> load_from_file(const String& path, const Size&);
|
||||||
|
@ -37,6 +38,7 @@ public:
|
||||||
private:
|
private:
|
||||||
#ifdef KERNEL
|
#ifdef KERNEL
|
||||||
GraphicsBitmap(Process&, const Size&);
|
GraphicsBitmap(Process&, const Size&);
|
||||||
|
GraphicsBitmap(const Size&);
|
||||||
#endif
|
#endif
|
||||||
GraphicsBitmap(const Size&, RGBA32*);
|
GraphicsBitmap(const Size&, RGBA32*);
|
||||||
|
|
||||||
|
|
111
WindowServer/WSMenu.cpp
Normal file
111
WindowServer/WSMenu.cpp
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
#include "WSMenu.h"
|
||||||
|
#include "WSMenuItem.h"
|
||||||
|
#include "WSWindow.h"
|
||||||
|
#include "WSMessage.h"
|
||||||
|
#include "WSMessageLoop.h"
|
||||||
|
#include <SharedGraphics/Painter.h>
|
||||||
|
#include <SharedGraphics/Font.h>
|
||||||
|
|
||||||
|
WSMenu::WSMenu(const String& name)
|
||||||
|
: m_name(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, 80) + padding() * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WSMenu::height() const
|
||||||
|
{
|
||||||
|
if (m_items.is_empty())
|
||||||
|
return 0;
|
||||||
|
return (m_items.last()->rect().bottom() - 1) + padding();
|
||||||
|
}
|
||||||
|
|
||||||
|
WSWindow& WSMenu::ensure_menu_window()
|
||||||
|
{
|
||||||
|
if (!m_menu_window) {
|
||||||
|
Point next_item_location(padding() / 2, 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() - padding(), height } });
|
||||||
|
next_item_location.move_by(0, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto window = make<WSWindow>(*this);
|
||||||
|
window->set_rect(0, 0, width(), height());
|
||||||
|
dbgprintf("Created menu window for menu '%s' (%u items) with rect %s\n", name().characters(), m_items.size(), window->rect().to_string().characters());
|
||||||
|
m_menu_window = move(window);
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
return *m_menu_window;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSMenu::draw()
|
||||||
|
{
|
||||||
|
ASSERT(m_menu_window);
|
||||||
|
ASSERT(m_menu_window->backing());
|
||||||
|
Painter painter(*m_menu_window->backing());
|
||||||
|
|
||||||
|
Rect rect { { }, m_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(), Color(0, 0, 128));
|
||||||
|
text_color = Color::White;
|
||||||
|
}
|
||||||
|
painter.draw_text(item->rect(), item->text(), Painter::TextAlignment::CenterLeft, text_color);
|
||||||
|
} else if (item->type() == WSMenuItem::Separator) {
|
||||||
|
Point p1(padding(), item->rect().center().y());
|
||||||
|
Point p2(width() - padding(), item->rect().center().y());
|
||||||
|
painter.draw_line(p1, p2, Color::MidGray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSMenu::on_window_message(WSMessage& message)
|
||||||
|
{
|
||||||
|
dbgprintf("WSMenu::on_window_message: %u\n", message.type());
|
||||||
|
if (message.type() == WSMessage::MouseMove) {
|
||||||
|
auto& mouse_event = static_cast<WSMouseEvent&>(message);
|
||||||
|
for (auto& item : m_items) {
|
||||||
|
if (item->rect().contains(mouse_event.position())) {
|
||||||
|
if (m_hovered_item == item.ptr())
|
||||||
|
return;
|
||||||
|
m_hovered_item = item.ptr();
|
||||||
|
draw();
|
||||||
|
menu_window()->invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
WindowServer/WSMenu.h
Normal file
66
WindowServer/WSMenu.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/AKString.h>
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
#include <SharedGraphics/Rect.h>
|
||||||
|
#include "WSMenuItem.h"
|
||||||
|
|
||||||
|
class WSMenuBar;
|
||||||
|
class WSMessage;
|
||||||
|
class WSWindow;
|
||||||
|
class Font;
|
||||||
|
|
||||||
|
class WSMenu {
|
||||||
|
public:
|
||||||
|
WSMenu(const String& name);
|
||||||
|
~WSMenu();
|
||||||
|
|
||||||
|
WSMenuBar* menu_bar() { return m_menubar; }
|
||||||
|
const WSMenuBar* menu_bar() const { return m_menubar; }
|
||||||
|
|
||||||
|
int item_count() const { return m_items.size(); }
|
||||||
|
WSMenuItem* item(int i) { return m_items[i].ptr(); }
|
||||||
|
const WSMenuItem* item(int i) const { return m_items[i].ptr(); }
|
||||||
|
|
||||||
|
void add_item(OwnPtr<WSMenuItem>&& item) { m_items.append(move(item)); }
|
||||||
|
|
||||||
|
String name() const { return m_name; }
|
||||||
|
|
||||||
|
template<typename Callback>
|
||||||
|
void for_each_item(Callback callback) const
|
||||||
|
{
|
||||||
|
for (auto& item : m_items)
|
||||||
|
callback(*item);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect text_rect_in_menubar() const { return m_text_rect_in_menubar; }
|
||||||
|
void set_text_rect_in_menubar(const Rect& rect) { m_text_rect_in_menubar = rect; }
|
||||||
|
|
||||||
|
Rect rect_in_menubar() const { return m_rect_in_menubar; }
|
||||||
|
void set_rect_in_menubar(const Rect& rect) { m_rect_in_menubar = rect; }
|
||||||
|
|
||||||
|
WSWindow* menu_window() { return m_menu_window.ptr(); }
|
||||||
|
void set_menu_window(OwnPtr<WSWindow>&&);
|
||||||
|
|
||||||
|
WSWindow& ensure_menu_window();
|
||||||
|
|
||||||
|
int width() const;
|
||||||
|
int height() const;
|
||||||
|
|
||||||
|
int item_height() const { return 16; }
|
||||||
|
int padding() const { return 4; }
|
||||||
|
|
||||||
|
void on_window_message(WSMessage&);
|
||||||
|
void draw();
|
||||||
|
const Font& font() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String m_name;
|
||||||
|
Rect m_rect_in_menubar;
|
||||||
|
Rect m_text_rect_in_menubar;
|
||||||
|
WSMenuBar* m_menubar { nullptr };
|
||||||
|
WSMenuItem* m_hovered_item { nullptr };
|
||||||
|
Vector<OwnPtr<WSMenuItem>> m_items;
|
||||||
|
OwnPtr<WSWindow> m_menu_window;
|
||||||
|
};
|
||||||
|
|
12
WindowServer/WSMenuBar.cpp
Normal file
12
WindowServer/WSMenuBar.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "WSMenuBar.h"
|
||||||
|
#include "WSMenu.h"
|
||||||
|
#include "WSMenuItem.h"
|
||||||
|
|
||||||
|
WSMenuBar::WSMenuBar()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WSMenuBar::~WSMenuBar()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
24
WindowServer/WSMenuBar.h
Normal file
24
WindowServer/WSMenuBar.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "WSMenu.h"
|
||||||
|
#include <AK/Vector.h>
|
||||||
|
|
||||||
|
class WSMenuBar {
|
||||||
|
public:
|
||||||
|
WSMenuBar();
|
||||||
|
~WSMenuBar();
|
||||||
|
|
||||||
|
void add_menu(OwnPtr<WSMenu>&& menu) { m_menus.append(move(menu)); }
|
||||||
|
|
||||||
|
template<typename Callback>
|
||||||
|
void for_each_menu(Callback callback)
|
||||||
|
{
|
||||||
|
for (auto& menu : m_menus) {
|
||||||
|
if (!callback(*menu))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<OwnPtr<WSMenu>> m_menus;
|
||||||
|
};
|
16
WindowServer/WSMenuItem.cpp
Normal file
16
WindowServer/WSMenuItem.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include "WSMenuItem.h"
|
||||||
|
|
||||||
|
WSMenuItem::WSMenuItem(const String& text)
|
||||||
|
: m_type(Text)
|
||||||
|
, m_text(text)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WSMenuItem::WSMenuItem(Type type)
|
||||||
|
: m_type(type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WSMenuItem::~WSMenuItem()
|
||||||
|
{
|
||||||
|
}
|
32
WindowServer/WSMenuItem.h
Normal file
32
WindowServer/WSMenuItem.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/AKString.h>
|
||||||
|
#include <SharedGraphics/Rect.h>
|
||||||
|
|
||||||
|
class WSMenuItem {
|
||||||
|
public:
|
||||||
|
enum Type {
|
||||||
|
None,
|
||||||
|
Text,
|
||||||
|
Separator,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit WSMenuItem(const String& text);
|
||||||
|
explicit WSMenuItem(Type);
|
||||||
|
~WSMenuItem();
|
||||||
|
|
||||||
|
Type type() const { return m_type; }
|
||||||
|
bool enabled() const { return m_enabled; }
|
||||||
|
|
||||||
|
String text() const { return m_text; }
|
||||||
|
|
||||||
|
void set_rect(const Rect& rect) { m_rect = rect; }
|
||||||
|
Rect rect() const { return m_rect; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type m_type { None };
|
||||||
|
bool m_enabled { true };
|
||||||
|
String m_text;
|
||||||
|
Rect m_rect;
|
||||||
|
};
|
||||||
|
|
|
@ -4,8 +4,15 @@
|
||||||
#include "WSMessageLoop.h"
|
#include "WSMessageLoop.h"
|
||||||
#include "Process.h"
|
#include "Process.h"
|
||||||
|
|
||||||
|
WSWindow::WSWindow(WSMenu& menu)
|
||||||
|
: m_lock("WSWindow (menu)")
|
||||||
|
, m_menu(&menu)
|
||||||
|
{
|
||||||
|
WSWindowManager::the().add_window(*this);
|
||||||
|
}
|
||||||
|
|
||||||
WSWindow::WSWindow(Process& process, int window_id)
|
WSWindow::WSWindow(Process& process, int window_id)
|
||||||
: m_lock("WSWindow")
|
: m_lock("WSWindow (normal)")
|
||||||
, m_process(&process)
|
, m_process(&process)
|
||||||
, m_window_id(window_id)
|
, m_window_id(window_id)
|
||||||
, m_pid(process.pid())
|
, m_pid(process.pid())
|
||||||
|
@ -35,13 +42,18 @@ void WSWindow::set_rect(const Rect& rect)
|
||||||
Rect old_rect;
|
Rect old_rect;
|
||||||
{
|
{
|
||||||
WSWindowLocker locker(*this);
|
WSWindowLocker locker(*this);
|
||||||
if (!m_process)
|
if (!m_process && !m_menu)
|
||||||
return;
|
return;
|
||||||
if (m_rect == rect)
|
if (m_rect == rect)
|
||||||
return;
|
return;
|
||||||
old_rect = m_rect;
|
old_rect = m_rect;
|
||||||
m_rect = rect;
|
m_rect = rect;
|
||||||
m_backing = GraphicsBitmap::create(*m_process, m_rect.size());
|
if (!m_backing || old_rect.size() != rect.size()) {
|
||||||
|
if (m_process)
|
||||||
|
m_backing = GraphicsBitmap::create(*m_process, m_rect.size());
|
||||||
|
if (m_menu)
|
||||||
|
m_backing = GraphicsBitmap::create_kernel_only(m_rect.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
WSWindowManager::the().notify_rect_changed(*this, old_rect, rect);
|
WSWindowManager::the().notify_rect_changed(*this, old_rect, rect);
|
||||||
}
|
}
|
||||||
|
@ -59,6 +71,14 @@ static GUI_MouseButton to_api(MouseButton button)
|
||||||
|
|
||||||
void WSWindow::on_message(WSMessage& message)
|
void WSWindow::on_message(WSMessage& message)
|
||||||
{
|
{
|
||||||
|
if (m_menu) {
|
||||||
|
m_menu->on_window_message(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_process)
|
||||||
|
return;
|
||||||
|
|
||||||
GUI_Event gui_event;
|
GUI_Event gui_event;
|
||||||
gui_event.window_id = window_id();
|
gui_event.window_id = window_id();
|
||||||
|
|
||||||
|
@ -149,3 +169,16 @@ void WSWindow::set_global_cursor_tracking_enabled(bool enabled)
|
||||||
dbgprintf("WSWindow{%p} global_cursor_tracking <- %u\n", enabled);
|
dbgprintf("WSWindow{%p} global_cursor_tracking <- %u\n", enabled);
|
||||||
m_global_cursor_tracking_enabled = enabled;
|
m_global_cursor_tracking_enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WSWindow::set_visible(bool b)
|
||||||
|
{
|
||||||
|
if (m_visible == b)
|
||||||
|
return;
|
||||||
|
m_visible = b;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSWindow::invalidate()
|
||||||
|
{
|
||||||
|
WSWindowManager::the().invalidate(*this);
|
||||||
|
}
|
||||||
|
|
|
@ -10,13 +10,16 @@
|
||||||
#include "WSMessageReceiver.h"
|
#include "WSMessageReceiver.h"
|
||||||
|
|
||||||
class Process;
|
class Process;
|
||||||
|
class WSMenu;
|
||||||
|
|
||||||
class WSWindow final : public WSMessageReceiver, public InlineLinkedListNode<WSWindow> {
|
class WSWindow final : public WSMessageReceiver, public InlineLinkedListNode<WSWindow> {
|
||||||
friend class WSWindowLocker;
|
friend class WSWindowLocker;
|
||||||
public:
|
public:
|
||||||
WSWindow(Process&, int window_id);
|
WSWindow(Process&, int window_id);
|
||||||
|
explicit WSWindow(WSMenu&);
|
||||||
virtual ~WSWindow() override;
|
virtual ~WSWindow() override;
|
||||||
|
|
||||||
|
bool is_menu() const { return m_menu; }
|
||||||
int window_id() const { return m_window_id; }
|
int window_id() const { return m_window_id; }
|
||||||
|
|
||||||
String title() const { return m_title; }
|
String title() const { return m_title; }
|
||||||
|
@ -27,14 +30,25 @@ public:
|
||||||
int width() const { return m_rect.width(); }
|
int width() const { return m_rect.width(); }
|
||||||
int height() const { return m_rect.height(); }
|
int height() const { return m_rect.height(); }
|
||||||
|
|
||||||
const Rect& rect() const { return m_rect; }
|
bool is_visible() const { return m_visible; }
|
||||||
|
void set_visible(bool);
|
||||||
|
|
||||||
|
Rect rect() const { return m_rect; }
|
||||||
void set_rect(const Rect&);
|
void set_rect(const Rect&);
|
||||||
|
void set_rect(int x, int y, int width, int height) { set_rect({ x, y, width, height }); }
|
||||||
void set_rect_without_repaint(const Rect& rect) { m_rect = rect; }
|
void set_rect_without_repaint(const Rect& rect) { m_rect = rect; }
|
||||||
|
|
||||||
|
void move_to(const Point& position) { set_rect({ position, size() }); }
|
||||||
|
void move_to(int x, int y) { move_to({ x, y }); }
|
||||||
|
|
||||||
Point position() const { return m_rect.location(); }
|
Point position() const { return m_rect.location(); }
|
||||||
void set_position(const Point& position) { set_rect({ position.x(), position.y(), width(), height() }); }
|
void set_position(const Point& position) { set_rect({ position.x(), position.y(), width(), height() }); }
|
||||||
void set_position_without_repaint(const Point& position) { set_rect_without_repaint({ position.x(), position.y(), width(), height() }); }
|
void set_position_without_repaint(const Point& position) { set_rect_without_repaint({ position.x(), position.y(), width(), height() }); }
|
||||||
|
|
||||||
|
Size size() const { return m_rect.size(); }
|
||||||
|
|
||||||
|
void invalidate();
|
||||||
|
|
||||||
virtual void on_message(WSMessage&) override;
|
virtual void on_message(WSMessage&) override;
|
||||||
|
|
||||||
bool is_being_dragged() const { return m_is_being_dragged; }
|
bool is_being_dragged() const { return m_is_being_dragged; }
|
||||||
|
@ -60,6 +74,9 @@ private:
|
||||||
Rect m_rect;
|
Rect m_rect;
|
||||||
bool m_is_being_dragged { false };
|
bool m_is_being_dragged { false };
|
||||||
bool m_global_cursor_tracking_enabled { false };
|
bool m_global_cursor_tracking_enabled { false };
|
||||||
|
bool m_visible { true };
|
||||||
|
|
||||||
|
WSMenu* m_menu { nullptr };
|
||||||
|
|
||||||
RetainPtr<GraphicsBitmap> m_backing;
|
RetainPtr<GraphicsBitmap> m_backing;
|
||||||
Process* m_process { nullptr };
|
Process* m_process { nullptr };
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
#include <SharedGraphics/CharacterBitmap.h>
|
#include <SharedGraphics/CharacterBitmap.h>
|
||||||
#include <AK/StdLibExtras.h>
|
#include <AK/StdLibExtras.h>
|
||||||
#include <Kernel/BochsVGADevice.h>
|
#include <Kernel/BochsVGADevice.h>
|
||||||
|
#include "WSMenu.h"
|
||||||
|
#include "WSMenuBar.h"
|
||||||
|
#include "WSMenuItem.h"
|
||||||
|
|
||||||
//#define DEBUG_COUNTERS
|
//#define DEBUG_COUNTERS
|
||||||
//#define DEBUG_WID_IN_TITLE_BAR
|
//#define DEBUG_WID_IN_TITLE_BAR
|
||||||
|
@ -176,6 +179,32 @@ WSWindowManager::WSWindowManager()
|
||||||
invalidate(m_screen_rect);
|
invalidate(m_screen_rect);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
auto menubar = make<WSMenuBar>();
|
||||||
|
|
||||||
|
{
|
||||||
|
auto menu = make<WSMenu>("Serenity");
|
||||||
|
menu->add_item(make<WSMenuItem>("Launch Terminal"));
|
||||||
|
menu->add_item(make<WSMenuItem>(WSMenuItem::Separator));
|
||||||
|
menu->add_item(make<WSMenuItem>("Hello again"));
|
||||||
|
menu->add_item(make<WSMenuItem>("To all my friends"));
|
||||||
|
menu->add_item(make<WSMenuItem>("Together we can play some rock&roll"));
|
||||||
|
menu->add_item(make<WSMenuItem>(WSMenuItem::Separator));
|
||||||
|
menu->add_item(make<WSMenuItem>("About..."));
|
||||||
|
menubar->add_menu(move(menu));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto menu = make<WSMenu>("Application");
|
||||||
|
menu->add_item(make<WSMenuItem>("Bar!"));
|
||||||
|
menu->add_item(make<WSMenuItem>("Foo!"));
|
||||||
|
menubar->add_menu(move(menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
set_current_menubar(menubar.ptr());
|
||||||
|
m_menubars.set(1, move(menubar));
|
||||||
|
}
|
||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
compose();
|
compose();
|
||||||
}
|
}
|
||||||
|
@ -184,6 +213,24 @@ WSWindowManager::~WSWindowManager()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WSWindowManager::set_current_menubar(WSMenuBar* menubar)
|
||||||
|
{
|
||||||
|
if (m_current_menubar == menubar)
|
||||||
|
return;
|
||||||
|
m_current_menubar = menubar;
|
||||||
|
if (!m_current_menubar)
|
||||||
|
return;
|
||||||
|
int menu_margin = 16;
|
||||||
|
Point next_menu_location { menu_margin / 2, 3 };
|
||||||
|
m_current_menubar->for_each_menu([&] (WSMenu& menu) {
|
||||||
|
int text_width = font().width(menu.name());
|
||||||
|
menu.set_rect_in_menubar({ next_menu_location.x() - menu_margin / 2, 0, text_width + menu_margin, menubar_rect().height() - 1 });
|
||||||
|
menu.set_text_rect_in_menubar({ next_menu_location, { text_width, font().glyph_height() } });
|
||||||
|
next_menu_location.move_by(menu.rect_in_menubar().width(), 0);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static const char* s_close_button_bitmap_data = {
|
static const char* s_close_button_bitmap_data = {
|
||||||
"## ##"
|
"## ##"
|
||||||
"### ###"
|
"### ###"
|
||||||
|
@ -205,6 +252,11 @@ void WSWindowManager::paint_window_frame(WSWindow& window)
|
||||||
LOCKER(m_lock);
|
LOCKER(m_lock);
|
||||||
//printf("[WM] paint_window_frame {%p}, rect: %d,%d %dx%d\n", &window, window.rect().x(), window.rect().y(), window.rect().width(), window.rect().height());
|
//printf("[WM] paint_window_frame {%p}, rect: %d,%d %dx%d\n", &window, window.rect().x(), window.rect().y(), window.rect().width(), window.rect().height());
|
||||||
|
|
||||||
|
if (window.is_menu()) {
|
||||||
|
m_back_painter->draw_rect(window.rect().inflated(2, 2), Color::LightGray);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto titlebar_rect = title_bar_rect(window.rect());
|
auto titlebar_rect = title_bar_rect(window.rect());
|
||||||
auto titlebar_inner_rect = title_bar_text_rect(window.rect());
|
auto titlebar_inner_rect = title_bar_text_rect(window.rect());
|
||||||
auto outer_rect = outer_window_rect(window.rect());
|
auto outer_rect = outer_window_rect(window.rect());
|
||||||
|
@ -318,6 +370,43 @@ void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect
|
||||||
invalidate(outer_window_rect(new_rect));
|
invalidate(outer_window_rect(new_rect));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WSWindowManager::handle_menu_mouse_event(WSMenu& menu, WSMouseEvent& event)
|
||||||
|
{
|
||||||
|
if (event.type() == WSMouseEvent::MouseDown && event.button() == MouseButton::Left) {
|
||||||
|
dbgprintf("[WM] MouseDown on menu '%s'\n", menu.name().characters());
|
||||||
|
if (m_current_menu == &menu)
|
||||||
|
return;
|
||||||
|
close_current_menu();
|
||||||
|
auto& menu_window = menu.ensure_menu_window();
|
||||||
|
menu_window.move_to({ menu.rect_in_menubar().x(), menu.rect_in_menubar().bottom() });
|
||||||
|
menu_window.set_visible(true);
|
||||||
|
m_current_menu = &menu;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.type() == WSMouseEvent::MouseUp && event.button() == MouseButton::Left) {
|
||||||
|
close_current_menu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSWindowManager::close_current_menu()
|
||||||
|
{
|
||||||
|
if (m_current_menu && m_current_menu->menu_window())
|
||||||
|
m_current_menu->menu_window()->set_visible(false);
|
||||||
|
m_current_menu = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSWindowManager::handle_menubar_mouse_event(WSMenuBar& menu, WSMouseEvent& event)
|
||||||
|
{
|
||||||
|
m_current_menubar->for_each_menu([&] (WSMenu& menu) {
|
||||||
|
if (menu.rect_in_menubar().contains(event.position())) {
|
||||||
|
handle_menu_mouse_event(menu, event);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void WSWindowManager::handle_titlebar_mouse_event(WSWindow& window, WSMouseEvent& event)
|
void WSWindowManager::handle_titlebar_mouse_event(WSWindow& window, WSMouseEvent& event)
|
||||||
{
|
{
|
||||||
if (event.type() == WSMessage::MouseDown && event.button() == MouseButton::Left) {
|
if (event.type() == WSMessage::MouseDown && event.button() == MouseButton::Left) {
|
||||||
|
@ -376,12 +465,23 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event)
|
||||||
for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
|
for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
|
||||||
if (!window->global_cursor_tracking())
|
if (!window->global_cursor_tracking())
|
||||||
continue;
|
continue;
|
||||||
|
ASSERT(window->is_visible()); // Maybe this should be supported? Idk. Let's catch it and think about it later.
|
||||||
Point position { event.x() - window->rect().x(), event.y() - window->rect().y() };
|
Point position { event.x() - window->rect().x(), event.y() - window->rect().y() };
|
||||||
auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button());
|
auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button());
|
||||||
window->on_message(*local_event);
|
window->on_message(*local_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_current_menubar && menubar_rect().contains(event.position())) {
|
||||||
|
handle_menubar_mouse_event(*m_current_menubar, event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_current_menu && event.type() == WSMouseEvent::MouseUp && event.button() == MouseButton::Left)
|
||||||
|
close_current_menu();
|
||||||
|
|
||||||
for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
|
for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
|
||||||
|
if (!window->is_visible())
|
||||||
|
continue;
|
||||||
if (title_bar_rect(window->rect()).contains(event.position())) {
|
if (title_bar_rect(window->rect()).contains(event.position())) {
|
||||||
if (event.type() == WSMessage::MouseDown) {
|
if (event.type() == WSMessage::MouseDown) {
|
||||||
move_to_front(*window);
|
move_to_front(*window);
|
||||||
|
@ -423,6 +523,8 @@ void WSWindowManager::compose()
|
||||||
|
|
||||||
auto any_window_contains_rect = [this] (const Rect& r) {
|
auto any_window_contains_rect = [this] (const Rect& r) {
|
||||||
for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
|
for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
|
||||||
|
if (!window->is_visible())
|
||||||
|
continue;
|
||||||
if (outer_window_rect(window->rect()).contains(r))
|
if (outer_window_rect(window->rect()).contains(r))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -449,6 +551,8 @@ void WSWindowManager::compose()
|
||||||
m_back_painter->blit(dirty_rect.location(), *m_wallpaper, dirty_rect);
|
m_back_painter->blit(dirty_rect.location(), *m_wallpaper, dirty_rect);
|
||||||
}
|
}
|
||||||
for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
|
for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
|
||||||
|
if (!window->is_visible())
|
||||||
|
continue;
|
||||||
WSWindowLocker locker(*window);
|
WSWindowLocker locker(*window);
|
||||||
RetainPtr<GraphicsBitmap> backing = window->backing();
|
RetainPtr<GraphicsBitmap> backing = window->backing();
|
||||||
if (!backing)
|
if (!backing)
|
||||||
|
@ -470,8 +574,9 @@ void WSWindowManager::compose()
|
||||||
}
|
}
|
||||||
m_back_painter->clear_clip_rect();
|
m_back_painter->clear_clip_rect();
|
||||||
}
|
}
|
||||||
draw_cursor();
|
|
||||||
|
|
||||||
|
draw_menubar();
|
||||||
|
draw_cursor();
|
||||||
|
|
||||||
if (m_flash_flush.lock_and_copy()) {
|
if (m_flash_flush.lock_and_copy()) {
|
||||||
for (auto& rect : dirty_rects)
|
for (auto& rect : dirty_rects)
|
||||||
|
@ -491,6 +596,28 @@ void WSWindowManager::invalidate_cursor()
|
||||||
invalidate(cursor_rect);
|
invalidate(cursor_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rect WSWindowManager::menubar_rect() const
|
||||||
|
{
|
||||||
|
return { 0, 0, m_screen_rect.width(), 16 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSWindowManager::draw_menubar()
|
||||||
|
{
|
||||||
|
if (!m_current_menubar)
|
||||||
|
return;
|
||||||
|
m_back_painter->fill_rect(menubar_rect(), Color::LightGray);
|
||||||
|
m_back_painter->draw_line({ 0, menubar_rect().bottom() }, { menubar_rect().right(), menubar_rect().bottom() }, Color::White);
|
||||||
|
m_current_menubar->for_each_menu([&] (WSMenu& menu) {
|
||||||
|
Color text_color = Color::Black;
|
||||||
|
if (&menu == m_current_menu) {
|
||||||
|
m_back_painter->fill_rect(menu.rect_in_menubar(), Color(0, 0, 128));
|
||||||
|
text_color = Color::White;
|
||||||
|
}
|
||||||
|
m_back_painter->draw_text(menu.text_rect_in_menubar(), menu.name(), Painter::TextAlignment::CenterLeft, text_color);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void WSWindowManager::draw_cursor()
|
void WSWindowManager::draw_cursor()
|
||||||
{
|
{
|
||||||
ASSERT_INTERRUPTS_ENABLED();
|
ASSERT_INTERRUPTS_ENABLED();
|
||||||
|
@ -619,3 +746,4 @@ void WSWindowManager::flush(const Rect& a_rect)
|
||||||
back_ptr = (RGBA32*)((byte*)back_ptr + pitch);
|
back_ptr = (RGBA32*)((byte*)back_ptr + pitch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,12 @@
|
||||||
#include <AK/InlineLinkedList.h>
|
#include <AK/InlineLinkedList.h>
|
||||||
#include <AK/WeakPtr.h>
|
#include <AK/WeakPtr.h>
|
||||||
#include <AK/Lock.h>
|
#include <AK/Lock.h>
|
||||||
#include <AK/CircularQueue.h>
|
#include <AK/HashMap.h>
|
||||||
#include "WSMessageReceiver.h"
|
#include "WSMessageReceiver.h"
|
||||||
|
#include "WSMenuBar.h"
|
||||||
|
|
||||||
class WSScreen;
|
class WSScreen;
|
||||||
|
class WSMenuBar;
|
||||||
class WSMouseEvent;
|
class WSMouseEvent;
|
||||||
class WSClientWantsToPaintMessage;
|
class WSClientWantsToPaintMessage;
|
||||||
class WSWindow;
|
class WSWindow;
|
||||||
|
@ -32,6 +34,13 @@ public:
|
||||||
|
|
||||||
void invalidate_cursor();
|
void invalidate_cursor();
|
||||||
void draw_cursor();
|
void draw_cursor();
|
||||||
|
void draw_menubar();
|
||||||
|
|
||||||
|
Rect menubar_rect() const;
|
||||||
|
WSMenuBar* current_menubar() { return m_current_menubar; }
|
||||||
|
void set_current_menubar(WSMenuBar*);
|
||||||
|
WSMenu* current_menu() { return m_current_menu; }
|
||||||
|
void set_current_menu(WSMenu*);
|
||||||
|
|
||||||
void invalidate(const WSWindow&);
|
void invalidate(const WSWindow&);
|
||||||
void invalidate(const WSWindow&, const Rect&);
|
void invalidate(const WSWindow&, const Rect&);
|
||||||
|
@ -47,10 +56,14 @@ private:
|
||||||
virtual ~WSWindowManager() override;
|
virtual ~WSWindowManager() override;
|
||||||
|
|
||||||
void process_mouse_event(WSMouseEvent&);
|
void process_mouse_event(WSMouseEvent&);
|
||||||
|
void handle_menu_mouse_event(WSMenu&, WSMouseEvent&);
|
||||||
|
void handle_menubar_mouse_event(WSMenuBar&, WSMouseEvent&);
|
||||||
void handle_titlebar_mouse_event(WSWindow&, WSMouseEvent&);
|
void handle_titlebar_mouse_event(WSWindow&, WSMouseEvent&);
|
||||||
void handle_close_button_mouse_event(WSWindow&, WSMouseEvent&);
|
void handle_close_button_mouse_event(WSWindow&, WSMouseEvent&);
|
||||||
|
|
||||||
void set_active_window(WSWindow*);
|
void set_active_window(WSWindow*);
|
||||||
|
|
||||||
|
void close_current_menu();
|
||||||
|
|
||||||
virtual void on_message(WSMessage&) override;
|
virtual void on_message(WSMessage&) override;
|
||||||
|
|
||||||
|
@ -112,4 +125,8 @@ private:
|
||||||
|
|
||||||
Lockable<bool> m_flash_flush;
|
Lockable<bool> m_flash_flush;
|
||||||
bool m_buffers_are_flipped { false };
|
bool m_buffers_are_flipped { false };
|
||||||
|
|
||||||
|
WSMenuBar* m_current_menubar { nullptr };
|
||||||
|
WSMenu* m_current_menu { nullptr };
|
||||||
|
HashMap<int, OwnPtr<WSMenuBar>> m_menubars;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue