MouseSettings: Add a tab to change cursor theme

This commit is contained in:
Maciej Zygmanowski 2021-08-02 12:14:52 +02:00 committed by Andreas Kling
parent 3597b6eb9d
commit 42df4fb2df
Notes: sideshowbarker 2024-07-18 05:21:31 +09:00
9 changed files with 388 additions and 76 deletions

View File

@ -4,14 +4,19 @@ serenity_component(
TARGETS MouseSettings
)
compile_gml(MouseSettingsWindow.gml MouseSettingsWindowGML.h mouse_settings_window_gml)
compile_gml(Mouse.gml MouseWidgetGML.h mouse_widget_gml)
compile_gml(Theme.gml ThemeWidgetGML.h theme_widget_gml)
set(SOURCES
main.cpp
DoubleClickArrowWidget.cpp
MouseSettingsWindow.cpp
MouseSettingsWindow.h
MouseSettingsWindowGML.h
MouseWidget.cpp
MouseWidget.h
MouseWidgetGML.h
ThemeWidget.cpp
ThemeWidget.h
ThemeWidgetGML.h
)
serenity_app(MouseSettings ICON app-mouse)

View File

@ -7,37 +7,13 @@
*/
#include "MouseSettingsWindow.h"
#include "DoubleClickArrowWidget.h"
#include <Applications/MouseSettings/MouseSettingsWindowGML.h>
#include "MouseWidget.h"
#include "ThemeWidget.h"
#include <LibGUI/Application.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Button.h>
#include <LibGUI/Label.h>
#include <LibGUI/TabWidget.h>
#include <LibGUI/Widget.h>
#include <LibGUI/WindowServerConnection.h>
#include <WindowServer/Screen.h>
#include <WindowServer/WindowManager.h>
constexpr double speed_slider_scale = 100.0;
constexpr int default_scroll_length = 4;
constexpr int double_click_speed_default = 250;
void MouseSettingsWindow::update_window_server()
{
const float factor = m_speed_slider->value() / speed_slider_scale;
GUI::WindowServerConnection::the().async_set_mouse_acceleration(factor);
GUI::WindowServerConnection::the().async_set_scroll_step_size(m_scroll_length_spinbox->value());
GUI::WindowServerConnection::the().async_set_double_click_speed(m_double_click_speed_slider->value());
}
void MouseSettingsWindow::reset_default_values()
{
m_speed_slider->set_value(speed_slider_scale);
m_scroll_length_spinbox->set_value(default_scroll_length);
m_double_click_speed_slider->set_value(double_click_speed_default);
update_window_server();
}
MouseSettingsWindow::MouseSettingsWindow()
{
@ -48,38 +24,9 @@ MouseSettingsWindow::MouseSettingsWindow()
main_widget.layout()->set_spacing(6);
auto& tab_widget = main_widget.add<GUI::TabWidget>();
auto& mouse_widget = tab_widget.add_tab<GUI::Widget>("Mouse");
mouse_widget.load_from_gml(mouse_settings_window_gml);
m_speed_label = *main_widget.find_descendant_of_type_named<GUI::Label>("speed_label");
m_speed_slider = *main_widget.find_descendant_of_type_named<GUI::HorizontalSlider>("speed_slider");
m_speed_slider->set_range(WindowServer::mouse_accel_min * speed_slider_scale, WindowServer::mouse_accel_max * speed_slider_scale);
m_speed_slider->on_change = [&](int value) {
m_speed_label->set_text(String::formatted("{} %", value));
};
const int slider_value = float { speed_slider_scale } * GUI::WindowServerConnection::the().get_mouse_acceleration();
m_speed_slider->set_value(slider_value);
auto& cursor_speed_image_label = *main_widget.find_descendant_of_type_named<GUI::Label>("cursor_speed_image_label");
cursor_speed_image_label.set_icon(Gfx::Bitmap::try_load_from_file("/res/graphics/mouse-cursor-speed.png"));
auto& scroll_step_size_image_label = *main_widget.find_descendant_of_type_named<GUI::Label>("scroll_step_size_image_label");
scroll_step_size_image_label.set_icon(Gfx::Bitmap::try_load_from_file("/res/graphics/scroll-wheel-step-size.png"));
m_scroll_length_spinbox = *main_widget.find_descendant_of_type_named<GUI::SpinBox>("scroll_length_spinbox");
m_scroll_length_spinbox->set_min(WindowServer::scroll_step_size_min);
m_scroll_length_spinbox->set_value(GUI::WindowServerConnection::the().get_scroll_step_size());
m_double_click_arrow_widget = *main_widget.find_descendant_of_type_named<MouseSettings::DoubleClickArrowWidget>("double_click_arrow_widget");
m_double_click_speed_label = *main_widget.find_descendant_of_type_named<GUI::Label>("double_click_speed_label");
m_double_click_speed_slider = *main_widget.find_descendant_of_type_named<GUI::HorizontalSlider>("double_click_speed_slider");
m_double_click_speed_slider->set_min(WindowServer::double_click_speed_min);
m_double_click_speed_slider->set_max(WindowServer::double_click_speed_max);
m_double_click_speed_slider->on_change = [&](int speed) {
m_double_click_arrow_widget->set_double_click_speed(speed);
m_double_click_speed_label->set_text(String::formatted("{} ms", speed));
};
m_double_click_speed_slider->set_value(GUI::WindowServerConnection::the().get_double_click_speed());
auto& mouse_widget = tab_widget.add_tab<MouseWidget>("Mouse");
auto& theme_widget = tab_widget.add_tab<ThemeWidget>("Cursor Theme");
auto& button_container = main_widget.add<GUI::Widget>();
button_container.set_shrink_to_fit(true);
@ -87,8 +34,9 @@ MouseSettingsWindow::MouseSettingsWindow()
button_container.layout()->set_spacing(6);
m_reset_button = button_container.add<GUI::Button>("Defaults");
m_reset_button->on_click = [this](auto) {
reset_default_values();
m_reset_button->on_click = [&](auto) {
mouse_widget.reset_default_values();
theme_widget.reset_default_values();
};
button_container.layout()->add_spacer();
@ -96,7 +44,8 @@ MouseSettingsWindow::MouseSettingsWindow()
m_ok_button = button_container.add<GUI::Button>("OK");
m_ok_button->set_fixed_width(75);
m_ok_button->on_click = [&](auto) {
update_window_server();
mouse_widget.update_window_server();
theme_widget.update_window_server();
GUI::Application::the()->quit();
};
@ -109,7 +58,8 @@ MouseSettingsWindow::MouseSettingsWindow()
m_apply_button = button_container.add<GUI::Button>("Apply");
m_apply_button->set_fixed_width(75);
m_apply_button->on_click = [&](auto) {
update_window_server();
mouse_widget.update_window_server();
theme_widget.update_window_server();
};
}

View File

@ -7,10 +7,7 @@
#pragma once
#include "DoubleClickArrowWidget.h"
#include <LibGUI/Button.h>
#include <LibGUI/Slider.h>
#include <LibGUI/SpinBox.h>
#include <LibGUI/Window.h>
class MouseSettingsWindow final : public GUI::Window {
@ -21,17 +18,8 @@ public:
private:
MouseSettingsWindow();
void update_window_server();
void reset_default_values();
RefPtr<GUI::HorizontalSlider> m_speed_slider;
RefPtr<GUI::Label> m_speed_label;
RefPtr<GUI::SpinBox> m_scroll_length_spinbox;
RefPtr<GUI::HorizontalSlider> m_double_click_speed_slider;
RefPtr<GUI::Label> m_double_click_speed_label;
RefPtr<GUI::Button> m_ok_button;
RefPtr<GUI::Button> m_cancel_button;
RefPtr<GUI::Button> m_apply_button;
RefPtr<GUI::Button> m_reset_button;
RefPtr<MouseSettings::DoubleClickArrowWidget> m_double_click_arrow_widget;
};

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "MouseWidget.h"
#include <Applications/MouseSettings/MouseWidgetGML.h>
#include <LibGUI/Label.h>
#include <LibGUI/Slider.h>
#include <LibGUI/SpinBox.h>
#include <LibGUI/WindowServerConnection.h>
#include <WindowServer/Screen.h>
#include <WindowServer/WindowManager.h>
constexpr double speed_slider_scale = 100.0;
constexpr int default_scroll_length = 4;
constexpr int double_click_speed_default = 250;
MouseWidget::MouseWidget()
{
load_from_gml(mouse_widget_gml);
m_speed_label = *find_descendant_of_type_named<GUI::Label>("speed_label");
m_speed_slider = *find_descendant_of_type_named<GUI::HorizontalSlider>("speed_slider");
m_speed_slider->set_range(WindowServer::mouse_accel_min * speed_slider_scale, WindowServer::mouse_accel_max * speed_slider_scale);
m_speed_slider->on_change = [&](int value) {
m_speed_label->set_text(String::formatted("{} %", value));
};
int const slider_value = float { speed_slider_scale } * GUI::WindowServerConnection::the().get_mouse_acceleration();
m_speed_slider->set_value(slider_value);
auto& cursor_speed_image_label = *find_descendant_of_type_named<GUI::Label>("cursor_speed_image_label");
cursor_speed_image_label.set_icon(Gfx::Bitmap::try_load_from_file("/res/graphics/mouse-cursor-speed.png"));
auto& scroll_step_size_image_label = *find_descendant_of_type_named<GUI::Label>("scroll_step_size_image_label");
scroll_step_size_image_label.set_icon(Gfx::Bitmap::try_load_from_file("/res/graphics/scroll-wheel-step-size.png"));
m_scroll_length_spinbox = *find_descendant_of_type_named<GUI::SpinBox>("scroll_length_spinbox");
m_scroll_length_spinbox->set_min(WindowServer::scroll_step_size_min);
m_scroll_length_spinbox->set_value(GUI::WindowServerConnection::the().get_scroll_step_size());
m_double_click_arrow_widget = *find_descendant_of_type_named<MouseSettings::DoubleClickArrowWidget>("double_click_arrow_widget");
m_double_click_speed_label = *find_descendant_of_type_named<GUI::Label>("double_click_speed_label");
m_double_click_speed_slider = *find_descendant_of_type_named<GUI::HorizontalSlider>("double_click_speed_slider");
m_double_click_speed_slider->set_min(WindowServer::double_click_speed_min);
m_double_click_speed_slider->set_max(WindowServer::double_click_speed_max);
m_double_click_speed_slider->on_change = [&](int speed) {
m_double_click_arrow_widget->set_double_click_speed(speed);
m_double_click_speed_label->set_text(String::formatted("{} ms", speed));
};
m_double_click_speed_slider->set_value(GUI::WindowServerConnection::the().get_double_click_speed());
}
void MouseWidget::update_window_server()
{
float const factor = m_speed_slider->value() / speed_slider_scale;
GUI::WindowServerConnection::the().async_set_mouse_acceleration(factor);
GUI::WindowServerConnection::the().async_set_scroll_step_size(m_scroll_length_spinbox->value());
GUI::WindowServerConnection::the().async_set_double_click_speed(m_double_click_speed_slider->value());
}
void MouseWidget::reset_default_values()
{
m_speed_slider->set_value(speed_slider_scale);
m_scroll_length_spinbox->set_value(default_scroll_length);
m_double_click_speed_slider->set_value(double_click_speed_default);
update_window_server();
}
MouseWidget::~MouseWidget()
{
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGUI/Window.h>
#include "DoubleClickArrowWidget.h"
class MouseWidget final : public GUI::Widget {
C_OBJECT(MouseWidget)
public:
virtual ~MouseWidget() override;
void update_window_server();
void reset_default_values();
private:
MouseWidget();
RefPtr<GUI::HorizontalSlider> m_speed_slider;
RefPtr<GUI::Label> m_speed_label;
RefPtr<GUI::SpinBox> m_scroll_length_spinbox;
RefPtr<GUI::HorizontalSlider> m_double_click_speed_slider;
RefPtr<GUI::Label> m_double_click_speed_label;
RefPtr<MouseSettings::DoubleClickArrowWidget> m_double_click_arrow_widget;
};

View File

@ -0,0 +1,39 @@
@GUI::Frame {
fill_with_background_color: true
layout: @GUI::VerticalBoxLayout {
margins: [10, 10, 10, 10]
}
@GUI::GroupBox {
title: "Theme"
layout: @GUI::VerticalBoxLayout {
margins: [16, 2, 16, 16]
}
@GUI::Widget {
fixed_height: 50
layout: @GUI::HorizontalBoxLayout {
spacing: 10
margins: [8, 16, 0, 8]
}
@GUI::Label {
text: "Select Theme: "
text_alignment: "CenterRight"
}
@GUI::ComboBox {
name: "theme_name_box"
model_only: true
}
}
@GUI::TableView {
name: "cursors_tableview"
font_size: 12
}
}
}

View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ThemeWidget.h"
#include <Applications/MouseSettings/ThemeWidgetGML.h>
#include <LibCore/DirIterator.h>
#include <LibGUI/Button.h>
#include <LibGUI/ComboBox.h>
#include <LibGUI/SortingProxyModel.h>
#include <LibGUI/TableView.h>
#include <LibGUI/WindowServerConnection.h>
String MouseCursorModel::column_name(int column_index) const
{
switch (column_index) {
case Column::Bitmap:
return {};
case Column::Name:
return "Name";
}
VERIFY_NOT_REACHED();
}
GUI::Variant MouseCursorModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const
{
auto& cursor = m_cursors[index.row()];
if (role == GUI::ModelRole::Display) {
switch (index.column()) {
case Column::Bitmap:
if (!cursor.bitmap)
return {};
return *cursor.bitmap;
case Column::Name:
return cursor.name;
}
VERIFY_NOT_REACHED();
}
return {};
}
void MouseCursorModel::invalidate()
{
if (m_theme_name.is_empty())
return;
m_cursors.clear();
Core::DirIterator iterator(String::formatted("/res/cursor-themes/{}", m_theme_name), Core::DirIterator::Flags::SkipDots);
while (iterator.has_next()) {
auto path = iterator.next_full_path();
if (path.ends_with(".ini"))
continue;
if (path.contains("2x"))
continue;
Cursor cursor;
cursor.path = move(path);
auto filename_split = cursor.path.split('/');
cursor.name = filename_split[3];
// FIXME: Animated cursor bitmaps
auto cursor_bitmap = Gfx::Bitmap::try_load_from_file(cursor.path);
auto cursor_bitmap_rect = cursor_bitmap->rect();
cursor.params = Gfx::CursorParams::parse_from_filename(cursor.name, cursor_bitmap_rect.center()).constrained(*cursor_bitmap);
cursor.bitmap = cursor_bitmap->cropped(Gfx::IntRect(Gfx::FloatRect(cursor_bitmap_rect).scaled(1.0 / cursor.params.frames(), 1.0)));
m_cursors.append(move(cursor));
}
Model::invalidate();
}
GUI::Variant ThemeModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const
{
if (role == GUI::ModelRole::Display) {
return m_themes[index.row()];
}
return {};
}
void ThemeModel::invalidate()
{
m_themes.clear();
Core::DirIterator iterator("/res/cursor-themes", Core::DirIterator::Flags::SkipDots);
while (iterator.has_next()) {
auto path = iterator.next_path();
if (access(String::formatted("/res/cursor-themes/{}/Config.ini", path).characters(), R_OK) == 0)
m_themes.append(path);
}
Model::invalidate();
}
ThemeWidget::ThemeWidget()
{
load_from_gml(theme_widget_gml);
m_cursors_tableview = find_descendant_of_type_named<GUI::TableView>("cursors_tableview");
m_cursors_tableview->set_highlight_selected_rows(true);
m_cursors_tableview->set_alternating_row_colors(false);
m_cursors_tableview->set_vertical_padding(16);
m_cursors_tableview->set_column_headers_visible(false);
m_cursors_tableview->set_highlight_key_column(false);
auto mouse_cursor_model = MouseCursorModel::create();
auto sorting_proxy_model = GUI::SortingProxyModel::create(mouse_cursor_model);
sorting_proxy_model->set_sort_role(GUI::ModelRole::Display);
m_cursors_tableview->set_model(sorting_proxy_model);
m_cursors_tableview->set_key_column_and_sort_order(MouseCursorModel::Column::Name, GUI::SortOrder::Ascending);
m_cursors_tableview->set_column_width(0, 25);
m_cursors_tableview->model()->invalidate();
m_theme_name = GUI::WindowServerConnection::the().get_cursor_theme();
mouse_cursor_model->change_theme(m_theme_name);
m_theme_name_box = find_descendant_of_type_named<GUI::ComboBox>("theme_name_box");
m_theme_name_box->on_change = [this, mouse_cursor_model](String const& value, GUI::ModelIndex const&) mutable {
m_theme_name = value;
mouse_cursor_model->change_theme(m_theme_name);
};
m_theme_name_box->set_model(ThemeModel::create());
m_theme_name_box->model()->invalidate();
m_theme_name_box->set_text(m_theme_name);
}
void ThemeWidget::update_window_server()
{
GUI::WindowServerConnection::the().async_apply_cursor_theme(m_theme_name_box->text());
}
void ThemeWidget::reset_default_values()
{
m_theme_name_box->set_text("Default");
update_window_server();
}
ThemeWidget::~ThemeWidget()
{
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGUI/Model.h>
#include <LibGUI/Window.h>
#include <LibGUI/WindowServerConnection.h>
#include <LibGfx/CursorParams.h>
#include "DoubleClickArrowWidget.h"
#include "LibGUI/FilePicker.h"
class MouseCursorModel final : public GUI::Model {
public:
static NonnullRefPtr<MouseCursorModel> create() { return adopt_ref(*new MouseCursorModel); }
virtual ~MouseCursorModel() override { }
enum Column {
Bitmap,
Name,
__Count,
};
virtual int row_count(const GUI::ModelIndex&) const override { return m_cursors.size(); }
virtual int column_count(const GUI::ModelIndex&) const override { return Column::__Count; }
virtual String column_name(int column_index) const override;
virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override;
virtual void invalidate() override;
void change_theme(String const& name)
{
m_theme_name = name;
invalidate();
}
private:
MouseCursorModel() { }
struct Cursor {
RefPtr<Gfx::Bitmap> bitmap;
String path;
String name;
Gfx::CursorParams params;
};
Vector<Cursor> m_cursors;
String m_theme_name;
};
class ThemeModel final : public GUI::Model {
public:
static NonnullRefPtr<ThemeModel> create() { return adopt_ref(*new ThemeModel); }
virtual int row_count(const GUI::ModelIndex&) const override { return m_themes.size(); }
virtual int column_count(const GUI::ModelIndex&) const override { return 1; }
virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override;
virtual void invalidate() override;
private:
Vector<String> m_themes;
};
class ThemeWidget final : public GUI::Widget {
C_OBJECT(ThemeWidget)
public:
virtual ~ThemeWidget() override;
void update_window_server();
void reset_default_values();
private:
ThemeWidget();
RefPtr<GUI::TableView> m_cursors_tableview;
RefPtr<GUI::ComboBox> m_theme_name_box;
String m_theme_name;
};