If there is a current menu, we now redirect all key events from window
manager to the menu manager. This allows us to properly navigate a menu
even when there is a current menu open.
Menu key navigation is now a lot more pleasant to use :^)
The action of pressing escape to close a menu has also been moved to its
proper home in menu manager in this commit.
set_current_menu() was indiscriminately closing all menus when the
current menu is not a submenu. We should only close menus not in lineage
to the one being closed.
m_current_menu was being set and then immediately cleared by
close_everyone(). Furthermore, since the menu being set can be a
nullptr, we now also make sure to handle that.
Finally, the logic can be simplified. close on the current menu is not
required, as that is also done by close_everyone().
Add event handling for key presses for navigating a menu. The currently
hovered menu item is tracked through an index which is either
incremented or decremented on up or down arrow key presses, changing the
hovered item.
Whenever there is a mouse move event, we ensure that the current index
matches the currently hovered item so that the mouse and keyboard do not
get out of sync.
If the right key is pressed, and we are on a submenu menu item, we
'enter' that submenu. While we are currently in a submenu, we forward
all keypress events to that submenu for handling. This allows us to
traverse the heirachy of a menu. While in a submenu, if the left key is
pressed, we leave that submenu and start handling the keypresses
ourselves again.
There is currently a small issue where the mouse hover and key hover can
get out of sync. The mouse can be traversing a submenu, but the parent
menu has no idea that the mouse has 'entered' a submenu, so will handle
the key presses itself, instead of forwarding them to the submenu. One
potential fix for this is for a menu to tell its menu parent that the
submenu is being traversed.
Menus are now owned by menu manager instead of being split between the
window manager and menu manager. If the window server wants to change
a menu, or call menu related functionality, this will need to be done
through the menu manager.
Further refactoring is likely needed, but this seems like a good start
for seperating menu logic from window logic.
Previously we would be left with a menu stack containing nulled-out
WeakPtr's to menus in the now-disconnected clients.
This was tripping up an assertion when clicking anywhere after shutting
down a program while it had a menu open.
Palette is now a value wrapper around a NonnullRefPtr<PaletteImpl>.
A new function, set_color(ColorRole, Color) implements a simple
copy-on-write mechanism so that we're sharing the PaletteImpl in the
common case, but allowing you to create custom palettes if you like,
by getting a GWidget's palette, modifying it, and then assigning the
modified palette to the widget via GWidget::set_palette().
Use this to make PaintBrush show its palette colors once again.
Fixes#943.
GApplication now has a palette. This palette contains all the system
theme colors by default, and is inherited by a new top-level GWidget.
New child widgets inherit their parents palette.
It is possible to override the GApplication palette, and the palette
of any GWidget.
The Palette object contains a bunch of colors, each corresponding to
a ColorRole. Each role has a convenience getter as well.
Each GWidget now has a background_role() and foreground_role(), which
are then looked up in their current palette when painting. This means
that you no longer alter the background color of a widget by setting
it directly, rather you alter either its background role, or the
widget's palette.
Color themes are loaded from .ini files in /res/themes/
The theme can be switched from the "Themes" section in the system menu.
The basic mechanism is that WindowServer broadcasts a SharedBuffer with
all of the color values of the current theme. Clients receive this with
the response to their initial WindowServer::Greet handshake.
When the theme is changed, WindowServer tells everyone by sending out
an UpdateSystemTheme message with a new SharedBuffer to use.
This does feel somewhat bloated somehow, but I'm sure we can iterate on
it over time and improve things.
To get one of the theme colors, use the Color(SystemColor) constructor:
painter.fill_rect(rect, SystemColor::HoverHighlight);
Some things don't work 100% right without a reboot. Specifically, when
constructing a GWidget, it will set its own background and foreground
colors based on the current SystemColor::Window and SystemColor::Text.
The widget is then stuck with these values, and they don't update on
system theme change, only on app restart.
All in all though, this is pretty cool. Merry Christmas! :^)
This patch introduces the second MenuApplet: Audio. To make this work,
menu applet windows now also receive mouse events.
There's still some problem with mute/unmute via clicking not actually
working, but the call goes from the applet program over IPC to the
AudioServer, where something goes wrong with the state change message.
Need to look at that separately.
Anyways, it's pretty cool to have more applets running in their own
separate processes. :^)
Instead of implementing menu applets as their own thing, they are now
WSWindows of WSWindowType::MenuApplet.
This makes it much easier to work with them on the client side, since
you can just create a GWindow with the right type and you're in the
menubar doing applet stuff :^)
Currently menu applets are laid out relative to the "audio rect" which
is the rect of the little audio muted state icon thingy.
There was an issue where applets would be placed at a negative X coord
if they were added to the WindowServer before the first time drawing
the menubar.
It's now possible to create a little applet window that sits inside the
system's menubar. This is done using the new CreateMenuApplet IPC call.
So far, it's possible to assign a backing store ID, and to invalidate
rects for repaint. There is no way to get the events from inside the
applet just yet.
This will allow us to move the CPU graph and audio thingy to separate
applet processes. :^)
Clicking on this icon toggles the AudioServer muted state.
It currently does not react to muted state changes caused by other
programs, since it has no way of learning about those from AudioServer,
other than performing a synchronous IPC call (GetMuted), which we don't
want to be doing in the WindowServer :^)
Let the global menu bar be either "open" or "closed". Clicking on one
of the menus in the menu bar toggles the state.
This ends up simpler and more intuitive than what we had before.
This patch moves a whole lot of the menu logic from WSWindowManager to
its proper home in WSMenuManager.
We also get rid of the "close_current_menu()" concept which was easily
confused in the presence of submenus. All operations should now be
aware of the menu stack instead. (The concept of a single, current menu
made a lot more sense when there were no nested menus.)
The menubar bar wasn't being resized correctly, as the underlying Window
was never being resized when `DisplayProperties` was chaning the
resolution while the system was running. `m_window` now gets the
correct window size when it's been updated, so a reboot isn't required.
Okay, I've spent a whole day on this now, and it finally kinda works!
With this patch, CObject and all of its derived classes are reference
counted instead of tree-owned.
The previous, Qt-like model was nice and familiar, but ultimately also
outdated and difficult to reason about.
CObject-derived types should now be stored in RefPtr/NonnullRefPtr and
each class can be constructed using the forwarding construct() helper:
auto widget = GWidget::construct(parent_widget);
Note that construct() simply forwards all arguments to an existing
constructor. It is inserted into each class by the C_OBJECT macro,
see CObject.h to understand how that works.
CObject::delete_later() disappears in this patch, as there is no longer
a single logical owner of a CObject.
It's now possible to add a GMenu as a submenu of another GMenu.
Simply use the GMenu::add_submenu(NonnullOwnPtr<GMenu>) API :^)
The WindowServer now keeps track of a stack of open menus rather than
just one "current menu". This code needs a bit more work, but the basic
functionality is now here!
Instead of LibGUI and WindowServer building their own copies of the drawing
and graphics code, let's it in a separate LibDraw library.
This avoids building the code twice, and will encourage better separation
of concerns. :^)