WindowServer: Add support for cursor themes
Now you can specify a CursorTheme key in /etc/WindowServer.ini. The cursors are loaded from /res/cursor-themes/<name> directory. This directory contains a Config.ini file with format similar to previous Cursor section, except it uses relative paths. This commit adds also Default theme, which uses cursors being previously in /res/cursors. The WidgetGallery is updated to match the new cursor path format.
|
@ -19,24 +19,7 @@ Name=Default
|
||||||
[Mouse]
|
[Mouse]
|
||||||
AccelerationFactor=1.0
|
AccelerationFactor=1.0
|
||||||
ScrollStepSize=4
|
ScrollStepSize=4
|
||||||
|
CursorTheme=Default
|
||||||
[Cursor]
|
|
||||||
Hidden=/res/cursors/hidden.png
|
|
||||||
Arrow=/res/cursors/arrow.x2y2.png
|
|
||||||
ResizeH=/res/cursors/resize-horizontal.png
|
|
||||||
ResizeV=/res/cursors/resize-vertical.png
|
|
||||||
ResizeDTLBR=/res/cursors/resize-diagonal-tlbr.png
|
|
||||||
ResizeDBLTR=/res/cursors/resize-diagonal-bltr.png
|
|
||||||
ResizeColumn=/res/cursors/resize-column.png
|
|
||||||
ResizeRow=/res/cursors/resize-row.png
|
|
||||||
IBeam=/res/cursors/i-beam.png
|
|
||||||
Disallowed=/res/cursors/disallowed.png
|
|
||||||
Move=/res/cursors/move.png
|
|
||||||
Hand=/res/cursors/hand.x8y4.png
|
|
||||||
Help=/res/cursors/help.x1y1.png
|
|
||||||
Drag=/res/cursors/drag.png
|
|
||||||
Wait=/res/cursors/wait.f14t100.png
|
|
||||||
Crosshair=/res/cursors/crosshair.png
|
|
||||||
|
|
||||||
[Graphics]
|
[Graphics]
|
||||||
OverlayRectShadow=/res/graphics/overlay-rect-shadow.png
|
OverlayRectShadow=/res/graphics/overlay-rect-shadow.png
|
||||||
|
|
17
Base/res/cursor-themes/Default/Config.ini
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[Cursor]
|
||||||
|
Hidden=hidden.png
|
||||||
|
Arrow=arrow.x2y2.png
|
||||||
|
ResizeH=resize-horizontal.png
|
||||||
|
ResizeV=resize-vertical.png
|
||||||
|
ResizeDTLBR=resize-diagonal-tlbr.png
|
||||||
|
ResizeDBLTR=resize-diagonal-bltr.png
|
||||||
|
ResizeColumn=resize-column.png
|
||||||
|
ResizeRow=resize-row.png
|
||||||
|
IBeam=i-beam.png
|
||||||
|
Disallowed=disallowed.png
|
||||||
|
Move=move.png
|
||||||
|
Hand=hand.x8y4.png
|
||||||
|
Help=help.x1y1.png
|
||||||
|
Drag=drag.png
|
||||||
|
Wait=wait.f14t100.png
|
||||||
|
Crosshair=crosshair.png
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 415 B After Width: | Height: | Size: 415 B |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 500 B |
Before Width: | Height: | Size: 205 B After Width: | Height: | Size: 205 B |
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 241 B |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 128 B After Width: | Height: | Size: 128 B |
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 711 B After Width: | Height: | Size: 711 B |
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 456 B |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
@ -10,6 +10,7 @@
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibCore/DirIterator.h>
|
#include <LibCore/DirIterator.h>
|
||||||
#include <LibGUI/Model.h>
|
#include <LibGUI/Model.h>
|
||||||
|
#include <LibGUI/WindowServerConnection.h>
|
||||||
|
|
||||||
class MouseCursorModel final : public GUI::Model {
|
class MouseCursorModel final : public GUI::Model {
|
||||||
public:
|
public:
|
||||||
|
@ -57,17 +58,19 @@ public:
|
||||||
{
|
{
|
||||||
m_cursors.clear();
|
m_cursors.clear();
|
||||||
|
|
||||||
Core::DirIterator iterator("/res/cursors", Core::DirIterator::Flags::SkipDots);
|
Core::DirIterator iterator(String::formatted("/res/cursor-themes/{}", GUI::WindowServerConnection::the().get_cursor_theme()), Core::DirIterator::Flags::SkipDots);
|
||||||
|
|
||||||
while (iterator.has_next()) {
|
while (iterator.has_next()) {
|
||||||
auto path = iterator.next_full_path();
|
auto path = iterator.next_full_path();
|
||||||
|
if (path.ends_with(".ini"))
|
||||||
|
continue;
|
||||||
if (path.contains("2x"))
|
if (path.contains("2x"))
|
||||||
continue;
|
continue;
|
||||||
Cursor cursor;
|
Cursor cursor;
|
||||||
cursor.path = move(path);
|
cursor.path = move(path);
|
||||||
cursor.bitmap = Gfx::Bitmap::try_load_from_file(cursor.path);
|
cursor.bitmap = Gfx::Bitmap::try_load_from_file(cursor.path);
|
||||||
auto filename_split = cursor.path.split('/');
|
auto filename_split = cursor.path.split('/');
|
||||||
cursor.name = filename_split[2];
|
cursor.name = filename_split[3];
|
||||||
m_cursors.append(move(cursor));
|
m_cursors.append(move(cursor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,12 +58,6 @@ WindowManager::~WindowManager()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Cursor> WindowManager::get_cursor(String const& name)
|
|
||||||
{
|
|
||||||
static auto const s_default_cursor_path = "/res/cursors/arrow.x2y2.png";
|
|
||||||
return Cursor::create(m_config->read_entry("Cursor", name, s_default_cursor_path), s_default_cursor_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WindowManager::reload_config()
|
void WindowManager::reload_config()
|
||||||
{
|
{
|
||||||
m_config = Core::ConfigFile::open("/etc/WindowServer.ini", Core::ConfigFile::AllowWriting::Yes);
|
m_config = Core::ConfigFile::open("/etc/WindowServer.ini", Core::ConfigFile::AllowWriting::Yes);
|
||||||
|
@ -77,31 +71,7 @@ void WindowManager::reload_config()
|
||||||
apply_virtual_desktop_settings(virtual_desktop_rows, virtual_desktop_columns, false);
|
apply_virtual_desktop_settings(virtual_desktop_rows, virtual_desktop_columns, false);
|
||||||
|
|
||||||
m_double_click_speed = m_config->read_num_entry("Input", "DoubleClickSpeed", 250);
|
m_double_click_speed = m_config->read_num_entry("Input", "DoubleClickSpeed", 250);
|
||||||
|
apply_cursor_theme(m_config->read_entry("Mouse", "CursorTheme", "Default"));
|
||||||
auto* current_cursor = Compositor::the().current_cursor();
|
|
||||||
auto reload_cursor = [&](RefPtr<Cursor>& cursor, const String& name) {
|
|
||||||
bool is_current_cursor = current_cursor && current_cursor == cursor.ptr();
|
|
||||||
cursor = get_cursor(name);
|
|
||||||
if (is_current_cursor)
|
|
||||||
Compositor::the().current_cursor_was_reloaded(cursor.ptr());
|
|
||||||
};
|
|
||||||
|
|
||||||
reload_cursor(m_hidden_cursor, "Hidden");
|
|
||||||
reload_cursor(m_arrow_cursor, "Arrow");
|
|
||||||
reload_cursor(m_hand_cursor, "Hand");
|
|
||||||
reload_cursor(m_help_cursor, "Help");
|
|
||||||
reload_cursor(m_resize_horizontally_cursor, "ResizeH");
|
|
||||||
reload_cursor(m_resize_vertically_cursor, "ResizeV");
|
|
||||||
reload_cursor(m_resize_diagonally_tlbr_cursor, "ResizeDTLBR");
|
|
||||||
reload_cursor(m_resize_diagonally_bltr_cursor, "ResizeDBLTR");
|
|
||||||
reload_cursor(m_resize_column_cursor, "ResizeColumn");
|
|
||||||
reload_cursor(m_resize_row_cursor, "ResizeRow");
|
|
||||||
reload_cursor(m_i_beam_cursor, "IBeam");
|
|
||||||
reload_cursor(m_disallowed_cursor, "Disallowed");
|
|
||||||
reload_cursor(m_move_cursor, "Move");
|
|
||||||
reload_cursor(m_drag_cursor, "Drag");
|
|
||||||
reload_cursor(m_wait_cursor, "Wait");
|
|
||||||
reload_cursor(m_crosshair_cursor, "Crosshair");
|
|
||||||
|
|
||||||
auto reload_graphic = [&](RefPtr<MultiScaleBitmaps>& bitmap, String const& name) {
|
auto reload_graphic = [&](RefPtr<MultiScaleBitmaps>& bitmap, String const& name) {
|
||||||
if (bitmap) {
|
if (bitmap) {
|
||||||
|
@ -2070,4 +2040,49 @@ WindowStack& WindowManager::get_rendering_window_stacks(WindowStack*& transition
|
||||||
return Compositor::the().get_rendering_window_stacks(transitioning_window_stack);
|
return Compositor::the().get_rendering_window_stacks(transitioning_window_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowManager::apply_cursor_theme(const String& theme_name)
|
||||||
|
{
|
||||||
|
auto cursor_theme_config = Core::ConfigFile::open(String::formatted("/res/cursor-themes/{}/{}", theme_name, "Config.ini"));
|
||||||
|
|
||||||
|
auto* current_cursor = Compositor::the().current_cursor();
|
||||||
|
auto reload_cursor = [&](RefPtr<Cursor>& cursor, const String& name) {
|
||||||
|
bool is_current_cursor = current_cursor && current_cursor == cursor.ptr();
|
||||||
|
|
||||||
|
static auto const s_default_cursor_path = "/res/cursor-themes/Default/arrow.x2y2.png";
|
||||||
|
cursor = Cursor::create(String::formatted("/res/cursor-themes/{}/{}", theme_name, cursor_theme_config->read_entry("Cursor", name)), s_default_cursor_path);
|
||||||
|
|
||||||
|
if (is_current_cursor) {
|
||||||
|
Compositor::the().current_cursor_was_reloaded(cursor.ptr());
|
||||||
|
|
||||||
|
if (m_hovered_window) {
|
||||||
|
if (auto* modal_window = const_cast<Window&>(*m_hovered_window).blocking_modal_window()) {
|
||||||
|
modal_window->set_cursor(cursor);
|
||||||
|
} else if (m_hovered_window->cursor()) {
|
||||||
|
m_hovered_window->set_cursor(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reload_cursor(m_hidden_cursor, "Hidden");
|
||||||
|
reload_cursor(m_arrow_cursor, "Arrow");
|
||||||
|
reload_cursor(m_hand_cursor, "Hand");
|
||||||
|
reload_cursor(m_help_cursor, "Help");
|
||||||
|
reload_cursor(m_resize_horizontally_cursor, "ResizeH");
|
||||||
|
reload_cursor(m_resize_vertically_cursor, "ResizeV");
|
||||||
|
reload_cursor(m_resize_diagonally_tlbr_cursor, "ResizeDTLBR");
|
||||||
|
reload_cursor(m_resize_diagonally_bltr_cursor, "ResizeDBLTR");
|
||||||
|
reload_cursor(m_resize_column_cursor, "ResizeColumn");
|
||||||
|
reload_cursor(m_resize_row_cursor, "ResizeRow");
|
||||||
|
reload_cursor(m_i_beam_cursor, "IBeam");
|
||||||
|
reload_cursor(m_disallowed_cursor, "Disallowed");
|
||||||
|
reload_cursor(m_move_cursor, "Move");
|
||||||
|
reload_cursor(m_drag_cursor, "Drag");
|
||||||
|
reload_cursor(m_wait_cursor, "Wait");
|
||||||
|
reload_cursor(m_crosshair_cursor, "Crosshair");
|
||||||
|
|
||||||
|
Compositor::the().invalidate_cursor();
|
||||||
|
m_config->write_entry("Mouse", "CursorTheme", theme_name);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,9 +315,9 @@ public:
|
||||||
|
|
||||||
MultiScaleBitmaps const* overlay_rect_shadow() const { return m_overlay_rect_shadow.ptr(); }
|
MultiScaleBitmaps const* overlay_rect_shadow() const { return m_overlay_rect_shadow.ptr(); }
|
||||||
|
|
||||||
private:
|
void apply_cursor_theme(String const& name);
|
||||||
RefPtr<Cursor> get_cursor(String const& name);
|
|
||||||
|
|
||||||
|
private:
|
||||||
void notify_new_active_window(Window&);
|
void notify_new_active_window(Window&);
|
||||||
void notify_new_active_input_window(Window&);
|
void notify_new_active_input_window(Window&);
|
||||||
void notify_previous_active_window(Window&);
|
void notify_previous_active_window(Window&);
|
||||||
|
|