2022-05-10 16:54:43 +03:00
/*
* Copyright ( c ) 2020 , Andreas Kling < kling @ serenityos . org >
2022-09-12 17:06:24 +03:00
* Copyright ( c ) 2021 - 2022 , networkException < networkexception @ serenityos . org >
2022-05-10 16:54:43 +03:00
* Copyright ( c ) 2021 - 2022 , Sam Atkins < atkinssj @ serenityos . org >
* Copyright ( c ) 2021 , Antonio Di Stefano < tonio9681 @ gmail . com >
* Copyright ( c ) 2022 , Filiph Sandström < filiph . sandstrom @ filfatstudios . com >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include "MainWidget.h"
2022-05-11 14:56:16 +03:00
# include <Applications/ThemeEditor/AlignmentPropertyGML.h>
# include <Applications/ThemeEditor/ColorPropertyGML.h>
# include <Applications/ThemeEditor/FlagPropertyGML.h>
# include <Applications/ThemeEditor/MetricPropertyGML.h>
# include <Applications/ThemeEditor/PathPropertyGML.h>
2022-05-10 16:54:43 +03:00
# include <Applications/ThemeEditor/ThemeEditorGML.h>
2023-03-21 18:35:30 +03:00
# include <LibFileSystem/FileSystem.h>
2022-05-10 16:54:43 +03:00
# include <LibFileSystemAccessClient/Client.h>
# include <LibGUI/ActionGroup.h>
# include <LibGUI/BoxLayout.h>
# include <LibGUI/Button.h>
2022-11-15 19:32:00 +03:00
# include <LibGUI/ConnectionToWindowServer.h>
2022-05-10 16:54:43 +03:00
# include <LibGUI/FilePicker.h>
# include <LibGUI/Frame.h>
2022-05-11 14:53:07 +03:00
# include <LibGUI/GroupBox.h>
2022-05-10 16:54:43 +03:00
# include <LibGUI/Icon.h>
# include <LibGUI/ItemListModel.h>
2022-05-11 14:56:16 +03:00
# include <LibGUI/Label.h>
2022-05-10 16:54:43 +03:00
# include <LibGUI/Menu.h>
# include <LibGUI/Menubar.h>
# include <LibGUI/MessageBox.h>
2022-05-11 14:56:16 +03:00
# include <LibGUI/ScrollableContainerWidget.h>
2022-05-10 16:54:43 +03:00
# include <LibGfx/Filters/ColorBlindnessFilter.h>
namespace ThemeEditor {
2022-05-11 14:56:16 +03:00
static const PropertyTab window_tab {
2023-03-10 23:20:56 +03:00
" Windows " sv ,
2022-05-10 16:54:43 +03:00
{
2022-05-11 14:53:07 +03:00
{ " General " ,
{ { Gfx : : FlagRole : : IsDark } ,
{ Gfx : : AlignmentRole : : TitleAlignment } ,
{ Gfx : : MetricRole : : TitleHeight } ,
{ Gfx : : MetricRole : : TitleButtonWidth } ,
{ Gfx : : MetricRole : : TitleButtonHeight } ,
{ Gfx : : PathRole : : TitleButtonIcons } ,
{ Gfx : : FlagRole : : TitleButtonsIconOnly } } } ,
{ " Border " ,
{ { Gfx : : MetricRole : : BorderThickness } ,
{ Gfx : : MetricRole : : BorderRadius } } } ,
{ " Active Window " ,
{ { Gfx : : ColorRole : : ActiveWindowBorder1 } ,
{ Gfx : : ColorRole : : ActiveWindowBorder2 } ,
{ Gfx : : ColorRole : : ActiveWindowTitle } ,
{ Gfx : : ColorRole : : ActiveWindowTitleShadow } ,
{ Gfx : : ColorRole : : ActiveWindowTitleStripes } ,
{ Gfx : : PathRole : : ActiveWindowShadow } } } ,
{ " Inactive Window " ,
{ { Gfx : : ColorRole : : InactiveWindowBorder1 } ,
{ Gfx : : ColorRole : : InactiveWindowBorder2 } ,
{ Gfx : : ColorRole : : InactiveWindowTitle } ,
{ Gfx : : ColorRole : : InactiveWindowTitleShadow } ,
{ Gfx : : ColorRole : : InactiveWindowTitleStripes } ,
{ Gfx : : PathRole : : InactiveWindowShadow } } } ,
{ " Highlighted Window " ,
{ { Gfx : : ColorRole : : HighlightWindowBorder1 } ,
{ Gfx : : ColorRole : : HighlightWindowBorder2 } ,
{ Gfx : : ColorRole : : HighlightWindowTitle } ,
{ Gfx : : ColorRole : : HighlightWindowTitleShadow } ,
{ Gfx : : ColorRole : : HighlightWindowTitleStripes } } } ,
{ " Moving Window " ,
{ { Gfx : : ColorRole : : MovingWindowBorder1 } ,
{ Gfx : : ColorRole : : MovingWindowBorder2 } ,
{ Gfx : : ColorRole : : MovingWindowTitle } ,
{ Gfx : : ColorRole : : MovingWindowTitleShadow } ,
{ Gfx : : ColorRole : : MovingWindowTitleStripes } } } ,
{ " Contents " ,
{ { Gfx : : ColorRole : : Window } ,
{ Gfx : : ColorRole : : WindowText } } } ,
{ " Desktop " ,
{ { Gfx : : ColorRole : : DesktopBackground } ,
{ Gfx : : PathRole : : TaskbarShadow } } } ,
2022-05-10 16:54:43 +03:00
}
} ;
2022-05-11 14:56:16 +03:00
static const PropertyTab widgets_tab {
2023-03-10 23:20:56 +03:00
" Widgets " sv ,
2022-05-10 16:54:43 +03:00
{
2022-05-11 14:53:07 +03:00
{ " General " ,
{ { Gfx : : ColorRole : : Accent } ,
{ Gfx : : ColorRole : : Base } ,
{ Gfx : : ColorRole : : ThreedHighlight } ,
{ Gfx : : ColorRole : : ThreedShadow1 } ,
{ Gfx : : ColorRole : : ThreedShadow2 } ,
{ Gfx : : ColorRole : : HoverHighlight } } } ,
{ " Text " ,
{ { Gfx : : ColorRole : : BaseText } ,
{ Gfx : : ColorRole : : DisabledTextFront } ,
{ Gfx : : ColorRole : : DisabledTextBack } ,
{ Gfx : : ColorRole : : PlaceholderText } } } ,
{ " Links " ,
{ { Gfx : : ColorRole : : Link } ,
{ Gfx : : ColorRole : : ActiveLink } ,
{ Gfx : : ColorRole : : VisitedLink } } } ,
{ " Buttons " ,
{ { Gfx : : ColorRole : : Button } ,
{ Gfx : : ColorRole : : ButtonText } } } ,
{ " Tooltips " ,
{ { Gfx : : ColorRole : : Tooltip } ,
{ Gfx : : ColorRole : : TooltipText } ,
{ Gfx : : PathRole : : TooltipShadow } } } ,
{ " Trays " ,
{ { Gfx : : ColorRole : : Tray } ,
{ Gfx : : ColorRole : : TrayText } } } ,
{ " Ruler " ,
{ { Gfx : : ColorRole : : Ruler } ,
{ Gfx : : ColorRole : : RulerBorder } ,
{ Gfx : : ColorRole : : RulerActiveText } ,
{ Gfx : : ColorRole : : RulerInactiveText } } } ,
{ " Gutter " ,
{ { Gfx : : ColorRole : : Gutter } ,
{ Gfx : : ColorRole : : GutterBorder } } } ,
{ " Rubber Band " ,
{ { Gfx : : ColorRole : : RubberBandBorder } ,
{ Gfx : : ColorRole : : RubberBandFill } } } ,
{ " Menus " ,
{ { Gfx : : ColorRole : : MenuBase } ,
{ Gfx : : ColorRole : : MenuBaseText } ,
{ Gfx : : ColorRole : : MenuSelection } ,
{ Gfx : : ColorRole : : MenuSelectionText } ,
{ Gfx : : ColorRole : : MenuStripe } ,
{ Gfx : : PathRole : : MenuShadow } } } ,
{ " Selection " ,
{ { Gfx : : ColorRole : : FocusOutline } ,
{ Gfx : : ColorRole : : TextCursor } ,
{ Gfx : : ColorRole : : Selection } ,
{ Gfx : : ColorRole : : SelectionText } ,
{ Gfx : : ColorRole : : InactiveSelection } ,
{ Gfx : : ColorRole : : InactiveSelectionText } ,
{ Gfx : : ColorRole : : HighlightSearching } ,
{ Gfx : : ColorRole : : HighlightSearchingText } } } ,
2022-05-10 16:54:43 +03:00
}
2022-05-11 14:56:16 +03:00
} ;
2022-05-10 16:54:43 +03:00
2022-05-11 14:56:16 +03:00
static const PropertyTab syntax_highlighting_tab {
2023-03-10 23:20:56 +03:00
" Syntax Highlighting " sv ,
2022-05-10 16:54:43 +03:00
{
2022-05-11 14:53:07 +03:00
{ " General " ,
{ { Gfx : : ColorRole : : SyntaxComment } ,
{ Gfx : : ColorRole : : SyntaxControlKeyword } ,
{ Gfx : : ColorRole : : SyntaxIdentifier } ,
{ Gfx : : ColorRole : : SyntaxKeyword } ,
{ Gfx : : ColorRole : : SyntaxNumber } ,
{ Gfx : : ColorRole : : SyntaxOperator } ,
{ Gfx : : ColorRole : : SyntaxPreprocessorStatement } ,
{ Gfx : : ColorRole : : SyntaxPreprocessorValue } ,
{ Gfx : : ColorRole : : SyntaxPunctuation } ,
{ Gfx : : ColorRole : : SyntaxString } ,
{ Gfx : : ColorRole : : SyntaxType } ,
{ Gfx : : ColorRole : : SyntaxFunction } ,
{ Gfx : : ColorRole : : SyntaxVariable } ,
{ Gfx : : ColorRole : : SyntaxCustomType } ,
{ Gfx : : ColorRole : : SyntaxNamespace } ,
{ Gfx : : ColorRole : : SyntaxMember } ,
{ Gfx : : ColorRole : : SyntaxParameter } } } ,
2022-05-10 16:54:43 +03:00
}
} ;
2022-12-02 21:11:39 +03:00
static const PropertyTab color_scheme_tab {
2023-03-10 23:20:56 +03:00
" Color Scheme " sv ,
2022-12-02 21:11:39 +03:00
{
{ " General " ,
2023-02-12 16:49:05 +03:00
{ { Gfx : : FlagRole : : BoldTextAsBright } ,
{ Gfx : : ColorRole : : Black } ,
2022-12-02 21:11:39 +03:00
{ Gfx : : ColorRole : : Red } ,
{ Gfx : : ColorRole : : Green } ,
{ Gfx : : ColorRole : : Yellow } ,
{ Gfx : : ColorRole : : Blue } ,
{ Gfx : : ColorRole : : Magenta } ,
2023-02-12 16:49:05 +03:00
{ Gfx : : ColorRole : : ColorSchemeBackground } ,
{ Gfx : : ColorRole : : ColorSchemeForeground } ,
2022-12-02 21:11:39 +03:00
{ Gfx : : ColorRole : : Cyan } ,
{ Gfx : : ColorRole : : White } ,
{ Gfx : : ColorRole : : BrightBlack } ,
{ Gfx : : ColorRole : : BrightRed } ,
{ Gfx : : ColorRole : : BrightGreen } ,
{ Gfx : : ColorRole : : BrightYellow } ,
{ Gfx : : ColorRole : : BrightBlue } ,
{ Gfx : : ColorRole : : BrightMagenta } ,
{ Gfx : : ColorRole : : BrightCyan } ,
{ Gfx : : ColorRole : : BrightWhite } } } ,
}
} ;
2022-12-30 15:13:23 +03:00
ErrorOr < NonnullRefPtr < MainWidget > > MainWidget : : try_create ( )
2022-05-10 16:54:43 +03:00
{
2022-12-30 15:13:23 +03:00
auto alignment_model = TRY ( AlignmentModel : : try_create ( ) ) ;
auto main_widget = TRY ( adopt_nonnull_ref_or_enomem ( new ( nothrow ) MainWidget ( move ( alignment_model ) ) ) ) ;
2023-01-07 15:38:23 +03:00
TRY ( main_widget - > load_from_gml ( theme_editor_gml ) ) ;
2022-12-30 15:13:23 +03:00
main_widget - > m_preview_widget = main_widget - > find_descendant_of_type_named < ThemeEditor : : PreviewWidget > ( " preview_widget " ) ;
main_widget - > m_property_tabs = main_widget - > find_descendant_of_type_named < GUI : : TabWidget > ( " property_tabs " ) ;
2022-05-10 16:54:43 +03:00
2022-12-30 15:13:23 +03:00
TRY ( main_widget - > add_property_tab ( window_tab ) ) ;
TRY ( main_widget - > add_property_tab ( widgets_tab ) ) ;
TRY ( main_widget - > add_property_tab ( syntax_highlighting_tab ) ) ;
TRY ( main_widget - > add_property_tab ( color_scheme_tab ) ) ;
2022-05-10 16:54:43 +03:00
2022-12-30 15:13:23 +03:00
main_widget - > build_override_controls ( ) ;
2022-06-16 21:29:25 +03:00
2022-12-30 15:13:23 +03:00
return main_widget ;
}
MainWidget : : MainWidget ( NonnullRefPtr < AlignmentModel > alignment_model )
: m_current_palette ( GUI : : Application : : the ( ) - > palette ( ) )
, m_alignment_model ( move ( alignment_model ) )
{
2022-05-10 16:54:43 +03:00
}
ErrorOr < void > MainWidget : : initialize_menubar ( GUI : : Window & window )
{
2023-04-16 12:56:01 +03:00
auto file_menu = TRY ( window . try_add_menu ( " &File " _short_string ) ) ;
2022-05-10 16:54:43 +03:00
TRY ( file_menu - > try_add_action ( GUI : : CommonActions : : make_open_action ( [ & ] ( auto & ) {
2022-11-05 18:19:52 +03:00
if ( request_close ( ) = = GUI : : Window : : CloseRequestDecision : : StayOpen )
return ;
2023-05-22 20:07:09 +03:00
auto response = FileSystemAccessClient : : Client : : the ( ) . open_file ( & window , " Select Theme " , " /res/themes " sv ) ;
2022-05-10 16:54:43 +03:00
if ( response . is_error ( ) )
return ;
2023-01-07 20:18:16 +03:00
auto load_from_file_result = load_from_file ( response . value ( ) . filename ( ) , response . value ( ) . release_stream ( ) ) ;
2022-12-06 19:26:13 +03:00
if ( load_from_file_result . is_error ( ) ) {
2023-01-07 20:18:16 +03:00
GUI : : MessageBox : : show_error ( & window , DeprecatedString : : formatted ( " Can't open file named {}: {} " , response . value ( ) . filename ( ) , load_from_file_result . error ( ) ) ) ;
2022-12-06 19:26:13 +03:00
return ;
}
2022-05-10 16:54:43 +03:00
} ) ) ) ;
m_save_action = GUI : : CommonActions : : make_save_action ( [ & ] ( auto & ) {
if ( m_path . has_value ( ) ) {
2023-02-09 05:02:46 +03:00
auto result = FileSystemAccessClient : : Client : : the ( ) . request_file ( & window , * m_path , Core : : File : : OpenMode : : ReadWrite | Core : : File : : OpenMode : : Truncate ) ;
2022-05-10 16:54:43 +03:00
if ( result . is_error ( ) )
return ;
2023-01-07 20:18:16 +03:00
save_to_file ( result . value ( ) . filename ( ) , result . value ( ) . release_stream ( ) ) ;
2022-05-10 16:54:43 +03:00
} else {
2023-02-09 05:02:46 +03:00
auto result = FileSystemAccessClient : : Client : : the ( ) . save_file ( & window , " Theme " , " ini " , Core : : File : : OpenMode : : ReadWrite | Core : : File : : OpenMode : : Truncate ) ;
2022-05-10 16:54:43 +03:00
if ( result . is_error ( ) )
return ;
2023-01-07 20:18:16 +03:00
save_to_file ( result . value ( ) . filename ( ) , result . value ( ) . release_stream ( ) ) ;
2022-05-10 16:54:43 +03:00
}
} ) ;
TRY ( file_menu - > try_add_action ( * m_save_action ) ) ;
TRY ( file_menu - > try_add_action ( GUI : : CommonActions : : make_save_as_action ( [ & ] ( auto & ) {
2023-02-09 05:02:46 +03:00
auto result = FileSystemAccessClient : : Client : : the ( ) . save_file ( & window , " Theme " , " ini " , Core : : File : : OpenMode : : ReadWrite | Core : : File : : OpenMode : : Truncate ) ;
2022-05-10 16:54:43 +03:00
if ( result . is_error ( ) )
return ;
2023-01-07 20:18:16 +03:00
save_to_file ( result . value ( ) . filename ( ) , result . value ( ) . release_stream ( ) ) ;
2022-05-10 16:54:43 +03:00
} ) ) ) ;
TRY ( file_menu - > try_add_separator ( ) ) ;
2022-05-21 01:23:08 +03:00
TRY ( file_menu - > try_add_action ( GUI : : CommonActions : : make_quit_action ( [ & ] ( auto & ) {
if ( request_close ( ) = = GUI : : Window : : CloseRequestDecision : : Close )
GUI : : Application : : the ( ) - > quit ( ) ;
} ) ) ) ;
2022-05-10 16:54:43 +03:00
2022-09-26 15:57:11 +03:00
TRY ( window . try_add_menu ( TRY ( GUI : : CommonMenus : : make_accessibility_menu ( * m_preview_widget ) ) ) ) ;
2022-05-10 16:54:43 +03:00
2023-04-16 12:56:01 +03:00
auto help_menu = TRY ( window . try_add_menu ( " &Help " _short_string ) ) ;
2022-10-14 23:27:30 +03:00
TRY ( help_menu - > try_add_action ( GUI : : CommonActions : : make_command_palette_action ( & window ) ) ) ;
2022-07-11 20:32:29 +03:00
TRY ( help_menu - > try_add_action ( GUI : : CommonActions : : make_about_action ( " Theme Editor " , GUI : : Icon : : default_icon ( " app-theme-editor " sv ) , & window ) ) ) ;
2022-05-10 16:54:43 +03:00
return { } ;
}
void MainWidget : : update_title ( )
{
2022-12-04 21:02:33 +03:00
window ( ) - > set_title ( DeprecatedString : : formatted ( " {}[*] - Theme Editor " , m_path . value_or ( " Untitled " ) ) ) ;
2022-05-10 16:54:43 +03:00
}
GUI : : Window : : CloseRequestDecision MainWidget : : request_close ( )
{
if ( ! window ( ) - > is_modified ( ) )
return GUI : : Window : : CloseRequestDecision : : Close ;
auto result = GUI : : MessageBox : : ask_about_unsaved_changes ( window ( ) , m_path . value_or ( " " ) , m_last_modified_time ) ;
if ( result = = GUI : : MessageBox : : ExecResult : : Yes ) {
m_save_action - > activate ( ) ;
if ( window ( ) - > is_modified ( ) )
return GUI : : Window : : CloseRequestDecision : : StayOpen ;
return GUI : : Window : : CloseRequestDecision : : Close ;
}
if ( result = = GUI : : MessageBox : : ExecResult : : No )
return GUI : : Window : : CloseRequestDecision : : Close ;
return GUI : : Window : : CloseRequestDecision : : StayOpen ;
}
2022-12-04 21:02:33 +03:00
void MainWidget : : set_path ( DeprecatedString path )
2022-05-10 16:54:43 +03:00
{
m_path = path ;
update_title ( ) ;
}
2023-02-09 05:02:46 +03:00
void MainWidget : : save_to_file ( String const & filename , NonnullOwnPtr < Core : : File > file )
2022-05-10 16:54:43 +03:00
{
2023-01-07 20:18:16 +03:00
auto theme = Core : : ConfigFile : : open ( filename . to_deprecated_string ( ) , move ( file ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2022-05-05 19:48:24 +03:00
2022-05-10 18:10:15 +03:00
# define __ENUMERATE_ALIGNMENT_ROLE(role) theme->write_entry("Alignments", to_string(Gfx::AlignmentRole::role), to_string(m_current_palette.alignment(Gfx::AlignmentRole::role)));
2022-05-11 14:56:16 +03:00
ENUMERATE_ALIGNMENT_ROLES ( __ENUMERATE_ALIGNMENT_ROLE )
# undef __ENUMERATE_ALIGNMENT_ROLE
2022-05-05 19:48:24 +03:00
2022-12-06 04:12:49 +03:00
# define __ENUMERATE_COLOR_ROLE(role) theme->write_entry("Colors", to_string(Gfx::ColorRole::role), m_current_palette.color(Gfx::ColorRole::role).to_deprecated_string());
2022-05-11 14:56:16 +03:00
ENUMERATE_COLOR_ROLES ( __ENUMERATE_COLOR_ROLE )
# undef __ENUMERATE_COLOR_ROLE
2022-05-10 16:54:43 +03:00
2022-05-10 18:10:15 +03:00
# define __ENUMERATE_FLAG_ROLE(role) theme->write_bool_entry("Flags", to_string(Gfx::FlagRole::role), m_current_palette.flag(Gfx::FlagRole::role));
2022-05-11 14:56:16 +03:00
ENUMERATE_FLAG_ROLES ( __ENUMERATE_FLAG_ROLE )
# undef __ENUMERATE_FLAG_ROLE
2022-05-10 16:54:43 +03:00
2022-05-10 18:10:15 +03:00
# define __ENUMERATE_METRIC_ROLE(role) theme->write_num_entry("Metrics", to_string(Gfx::MetricRole::role), m_current_palette.metric(Gfx::MetricRole::role));
2022-05-11 14:56:16 +03:00
ENUMERATE_METRIC_ROLES ( __ENUMERATE_METRIC_ROLE )
# undef __ENUMERATE_METRIC_ROLE
2022-05-10 16:54:43 +03:00
2022-05-10 18:10:15 +03:00
# define __ENUMERATE_PATH_ROLE(role) theme->write_entry("Paths", to_string(Gfx::PathRole::role), m_current_palette.path(Gfx::PathRole::role));
2022-05-11 14:56:16 +03:00
ENUMERATE_PATH_ROLES ( __ENUMERATE_PATH_ROLE )
# undef __ENUMERATE_PATH_ROLE
2022-05-10 16:54:43 +03:00
auto sync_result = theme - > sync ( ) ;
if ( sync_result . is_error ( ) ) {
2022-12-04 21:02:33 +03:00
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Failed to save theme file: {} " , sync_result . error ( ) ) ) ;
2022-05-10 16:54:43 +03:00
} else {
2023-03-13 18:30:34 +03:00
m_last_modified_time = Duration : : now_monotonic ( ) ;
2023-01-07 20:18:16 +03:00
set_path ( filename . to_deprecated_string ( ) ) ;
2022-05-10 16:54:43 +03:00
window ( ) - > set_modified ( false ) ;
}
}
2022-06-16 21:29:25 +03:00
ErrorOr < Core : : AnonymousBuffer > MainWidget : : encode ( )
{
auto buffer = TRY ( Core : : AnonymousBuffer : : create_with_size ( sizeof ( Gfx : : SystemTheme ) ) ) ;
auto * data = buffer . data < Gfx : : SystemTheme > ( ) ;
# define __ENUMERATE_ALIGNMENT_ROLE(role) \
data - > alignment [ ( int ) Gfx : : AlignmentRole : : role ] = m_current_palette . alignment ( Gfx : : AlignmentRole : : role ) ;
ENUMERATE_ALIGNMENT_ROLES ( __ENUMERATE_ALIGNMENT_ROLE )
# undef __ENUMERATE_ALIGNMENT_ROLE
# define __ENUMERATE_COLOR_ROLE(role) \
data - > color [ ( int ) Gfx : : ColorRole : : role ] = m_current_palette . color ( Gfx : : ColorRole : : role ) . value ( ) ;
ENUMERATE_COLOR_ROLES ( __ENUMERATE_COLOR_ROLE )
# undef __ENUMERATE_COLOR_ROLE
# define __ENUMERATE_FLAG_ROLE(role) \
data - > flag [ ( int ) Gfx : : FlagRole : : role ] = m_current_palette . flag ( Gfx : : FlagRole : : role ) ;
ENUMERATE_FLAG_ROLES ( __ENUMERATE_FLAG_ROLE )
# undef __ENUMERATE_FLAG_ROLE
# define __ENUMERATE_METRIC_ROLE(role) \
data - > metric [ ( int ) Gfx : : MetricRole : : role ] = m_current_palette . metric ( Gfx : : MetricRole : : role ) ;
ENUMERATE_METRIC_ROLES ( __ENUMERATE_METRIC_ROLE )
# undef __ENUMERATE_METRIC_ROLE
# define ENCODE_PATH(role, allow_empty) \
do { \
auto path = m_current_palette . path ( Gfx : : PathRole : : role ) ; \
char const * characters ; \
if ( path . is_empty ( ) ) { \
switch ( Gfx : : PathRole : : role ) { \
case Gfx : : PathRole : : TitleButtonIcons : \
characters = " /res/icons/16x16/ " ; \
break ; \
default : \
characters = allow_empty ? " " : " /res/ " ; \
} \
} \
characters = path . characters ( ) ; \
memcpy ( data - > path [ ( int ) Gfx : : PathRole : : role ] , characters , min ( strlen ( characters ) + 1 , sizeof ( data - > path [ ( int ) Gfx : : PathRole : : role ] ) ) ) ; \
data - > path [ ( int ) Gfx : : PathRole : : role ] [ sizeof ( data - > path [ ( int ) Gfx : : PathRole : : role ] ) - 1 ] = ' \0 ' ; \
} while ( 0 )
ENCODE_PATH ( TitleButtonIcons , false ) ;
ENCODE_PATH ( ActiveWindowShadow , true ) ;
ENCODE_PATH ( InactiveWindowShadow , true ) ;
ENCODE_PATH ( TaskbarShadow , true ) ;
ENCODE_PATH ( MenuShadow , true ) ;
ENCODE_PATH ( TooltipShadow , true ) ;
return buffer ;
}
void MainWidget : : build_override_controls ( )
{
auto * theme_override_controls = find_descendant_of_type_named < GUI : : Widget > ( " theme_override_controls " ) ;
2022-09-26 15:58:00 +03:00
m_theme_override_apply = theme_override_controls - > find_child_of_type_named < GUI : : DialogButton > ( " apply_button " ) ;
m_theme_override_reset = theme_override_controls - > find_child_of_type_named < GUI : : DialogButton > ( " reset_button " ) ;
2022-06-16 21:29:25 +03:00
m_theme_override_apply - > on_click = [ & ] ( auto ) {
auto encoded = encode ( ) ;
if ( encoded . is_error ( ) )
return ;
2023-02-12 16:48:23 +03:00
// Empty the color scheme path to signal that it exists only in memory.
m_current_palette . path ( Gfx : : PathRole : : ColorScheme ) = " " ;
2022-06-16 21:29:25 +03:00
GUI : : ConnectionToWindowServer : : the ( ) . async_set_system_theme_override ( encoded . value ( ) ) ;
} ;
m_theme_override_reset - > on_click = [ & ] ( auto ) {
GUI : : ConnectionToWindowServer : : the ( ) . async_clear_system_theme_override ( ) ;
} ;
GUI : : Application : : the ( ) - > on_theme_change = [ & ] ( ) {
auto override_active = GUI : : ConnectionToWindowServer : : the ( ) . is_system_theme_overridden ( ) ;
m_theme_override_apply - > set_enabled ( ! override_active & & window ( ) - > is_modified ( ) ) ;
m_theme_override_reset - > set_enabled ( override_active ) ;
} ;
}
2022-12-30 15:13:23 +03:00
ErrorOr < void > MainWidget : : add_property_tab ( PropertyTab const & property_tab )
2022-05-11 14:56:16 +03:00
{
2023-03-10 23:20:56 +03:00
auto scrollable_container = TRY ( m_property_tabs - > try_add_tab < GUI : : ScrollableContainerWidget > ( TRY ( String : : from_utf8 ( property_tab . title ) ) ) ) ;
2022-12-30 15:13:23 +03:00
scrollable_container - > set_should_hide_unnecessary_scrollbars ( true ) ;
2022-05-11 14:56:16 +03:00
2022-12-30 15:13:23 +03:00
auto properties_list = TRY ( GUI : : Widget : : try_create ( ) ) ;
scrollable_container - > set_widget ( properties_list ) ;
2023-02-17 00:17:12 +03:00
TRY ( properties_list - > try_set_layout < GUI : : VerticalBoxLayout > ( GUI : : Margins { 8 } , 12 ) ) ;
2022-05-11 14:56:16 +03:00
2022-05-11 14:53:07 +03:00
for ( auto const & group : property_tab . property_groups ) {
2022-12-30 15:13:23 +03:00
NonnullRefPtr < GUI : : GroupBox > group_box = TRY ( properties_list - > try_add < GUI : : GroupBox > ( group . title ) ) ;
2022-05-11 14:53:07 +03:00
// 1px less on the left makes the text line up with the group title.
2023-02-17 00:17:12 +03:00
TRY ( group_box - > try_set_layout < GUI : : VerticalBoxLayout > ( GUI : : Margins { 8 , 8 , 8 , 7 } , 12 ) ) ;
2022-05-26 16:34:32 +03:00
group_box - > set_preferred_height ( GUI : : SpecialDimension : : Fit ) ;
2022-05-11 14:53:07 +03:00
for ( auto const & property : group . properties ) {
2022-12-30 15:13:23 +03:00
NonnullRefPtr < GUI : : Widget > row_widget = TRY ( group_box - > try_add < GUI : : Widget > ( ) ) ;
2022-05-11 14:53:07 +03:00
row_widget - > set_fixed_height ( 22 ) ;
2022-12-30 15:13:23 +03:00
TRY ( property . role . visit (
[ & ] ( Gfx : : AlignmentRole role ) - > ErrorOr < void > {
2023-01-07 15:38:23 +03:00
TRY ( row_widget - > load_from_gml ( alignment_property_gml ) ) ;
2022-05-11 14:53:07 +03:00
auto & name_label = * row_widget - > find_descendant_of_type_named < GUI : : Label > ( " name " ) ;
2023-04-29 17:41:48 +03:00
name_label . set_text ( TRY ( String : : from_utf8 ( to_string ( role ) ) ) ) ;
2022-05-11 14:53:07 +03:00
auto & alignment_picker = * row_widget - > find_descendant_of_type_named < GUI : : ComboBox > ( " combo_box " ) ;
alignment_picker . set_model ( * m_alignment_model ) ;
alignment_picker . on_change = [ & , role ] ( auto & , auto & index ) {
set_alignment ( role , index . data ( GUI : : ModelRole : : Custom ) . to_text_alignment ( Gfx : : TextAlignment : : CenterLeft ) ) ;
} ;
2022-05-10 18:10:15 +03:00
alignment_picker . set_selected_index ( m_alignment_model - > index_of ( m_current_palette . alignment ( role ) ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:53:07 +03:00
VERIFY ( m_alignment_inputs [ to_underlying ( role ) ] . is_null ( ) ) ;
m_alignment_inputs [ to_underlying ( role ) ] = alignment_picker ;
2022-12-30 15:13:23 +03:00
return { } ;
2022-05-11 14:53:07 +03:00
} ,
2022-12-30 15:13:23 +03:00
[ & ] ( Gfx : : ColorRole role ) - > ErrorOr < void > {
2023-01-07 15:38:23 +03:00
TRY ( row_widget - > load_from_gml ( color_property_gml ) ) ;
2022-05-11 14:53:07 +03:00
auto & name_label = * row_widget - > find_descendant_of_type_named < GUI : : Label > ( " name " ) ;
2023-04-29 17:41:48 +03:00
name_label . set_text ( TRY ( String : : from_utf8 ( to_string ( role ) ) ) ) ;
2022-05-11 14:53:07 +03:00
auto & color_input = * row_widget - > find_descendant_of_type_named < GUI : : ColorInput > ( " color_input " ) ;
color_input . on_change = [ & , role ] {
set_color ( role , color_input . color ( ) ) ;
} ;
2022-05-10 18:10:15 +03:00
color_input . set_color ( m_current_palette . color ( role ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:53:07 +03:00
VERIFY ( m_color_inputs [ to_underlying ( role ) ] . is_null ( ) ) ;
m_color_inputs [ to_underlying ( role ) ] = color_input ;
2022-12-30 15:13:23 +03:00
return { } ;
2022-05-11 14:53:07 +03:00
} ,
2022-12-30 15:13:23 +03:00
[ & ] ( Gfx : : FlagRole role ) - > ErrorOr < void > {
2023-01-07 15:38:23 +03:00
TRY ( row_widget - > load_from_gml ( flag_property_gml ) ) ;
2022-05-11 14:53:07 +03:00
auto & checkbox = * row_widget - > find_descendant_of_type_named < GUI : : CheckBox > ( " checkbox " ) ;
2023-05-06 16:54:35 +03:00
checkbox . set_text ( TRY ( String : : from_utf8 ( to_string ( role ) ) ) ) ;
2022-05-11 14:53:07 +03:00
checkbox . on_checked = [ & , role ] ( bool checked ) {
set_flag ( role , checked ) ;
} ;
2022-05-10 18:10:15 +03:00
checkbox . set_checked ( m_current_palette . flag ( role ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:53:07 +03:00
VERIFY ( m_flag_inputs [ to_underlying ( role ) ] . is_null ( ) ) ;
m_flag_inputs [ to_underlying ( role ) ] = checkbox ;
2022-12-30 15:13:23 +03:00
return { } ;
2022-05-11 14:53:07 +03:00
} ,
2022-12-30 15:13:23 +03:00
[ & ] ( Gfx : : MetricRole role ) - > ErrorOr < void > {
2023-01-07 15:38:23 +03:00
TRY ( row_widget - > load_from_gml ( metric_property_gml ) ) ;
2022-05-11 14:53:07 +03:00
auto & name_label = * row_widget - > find_descendant_of_type_named < GUI : : Label > ( " name " ) ;
2023-04-29 17:41:48 +03:00
name_label . set_text ( TRY ( String : : from_utf8 ( to_string ( role ) ) ) ) ;
2022-05-11 14:53:07 +03:00
auto & spin_box = * row_widget - > find_descendant_of_type_named < GUI : : SpinBox > ( " spin_box " ) ;
spin_box . on_change = [ & , role ] ( int value ) {
set_metric ( role , value ) ;
} ;
2022-05-10 18:10:15 +03:00
spin_box . set_value ( m_current_palette . metric ( role ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:53:07 +03:00
VERIFY ( m_metric_inputs [ to_underlying ( role ) ] . is_null ( ) ) ;
m_metric_inputs [ to_underlying ( role ) ] = spin_box ;
2022-12-30 15:13:23 +03:00
return { } ;
2022-05-11 14:53:07 +03:00
} ,
2022-12-30 15:13:23 +03:00
[ & ] ( Gfx : : PathRole role ) - > ErrorOr < void > {
2023-01-07 15:38:23 +03:00
TRY ( row_widget - > load_from_gml ( path_property_gml ) ) ;
2022-05-11 14:53:07 +03:00
auto & name_label = * row_widget - > find_descendant_of_type_named < GUI : : Label > ( " name " ) ;
2023-04-29 17:41:48 +03:00
name_label . set_text ( TRY ( String : : from_utf8 ( to_string ( role ) ) ) ) ;
2022-05-11 14:53:07 +03:00
auto & path_input = * row_widget - > find_descendant_of_type_named < GUI : : TextBox > ( " path_input " ) ;
path_input . on_change = [ & , role ] {
set_path ( role , path_input . text ( ) ) ;
} ;
2022-05-10 18:10:15 +03:00
path_input . set_text ( m_current_palette . path ( role ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:53:07 +03:00
auto & path_picker_button = * row_widget - > find_descendant_of_type_named < GUI : : Button > ( " path_picker_button " ) ;
auto picker_target = ( role = = Gfx : : PathRole : : TitleButtonIcons ) ? PathPickerTarget : : Folder : PathPickerTarget : : File ;
path_picker_button . on_click = [ & , role , picker_target ] ( auto ) {
show_path_picker_dialog ( to_string ( role ) , path_input , picker_target ) ;
} ;
VERIFY ( m_path_inputs [ to_underlying ( role ) ] . is_null ( ) ) ;
m_path_inputs [ to_underlying ( role ) ] = path_input ;
2022-12-30 15:13:23 +03:00
return { } ;
} ) ) ;
2022-05-11 14:53:07 +03:00
}
2022-05-11 14:56:16 +03:00
}
2022-12-30 15:13:23 +03:00
return { } ;
2022-05-11 14:56:16 +03:00
}
void MainWidget : : set_alignment ( Gfx : : AlignmentRole role , Gfx : : TextAlignment value )
{
2022-05-10 18:10:15 +03:00
auto preview_palette = m_current_palette ;
2022-05-11 14:56:16 +03:00
preview_palette . set_alignment ( role , value ) ;
2022-05-10 18:10:15 +03:00
set_palette ( preview_palette ) ;
2022-05-11 14:56:16 +03:00
}
void MainWidget : : set_color ( Gfx : : ColorRole role , Gfx : : Color value )
{
2022-05-10 18:10:15 +03:00
auto preview_palette = m_current_palette ;
2022-05-11 14:56:16 +03:00
preview_palette . set_color ( role , value ) ;
2022-05-10 18:10:15 +03:00
set_palette ( preview_palette ) ;
2022-05-11 14:56:16 +03:00
}
void MainWidget : : set_flag ( Gfx : : FlagRole role , bool value )
{
2022-05-10 18:10:15 +03:00
auto preview_palette = m_current_palette ;
2022-05-11 14:56:16 +03:00
preview_palette . set_flag ( role , value ) ;
2022-05-10 18:10:15 +03:00
set_palette ( preview_palette ) ;
2022-05-11 14:56:16 +03:00
}
void MainWidget : : set_metric ( Gfx : : MetricRole role , int value )
{
2022-05-10 18:10:15 +03:00
auto preview_palette = m_current_palette ;
2022-05-11 14:56:16 +03:00
preview_palette . set_metric ( role , value ) ;
2022-05-10 18:10:15 +03:00
set_palette ( preview_palette ) ;
2022-05-11 14:56:16 +03:00
}
2022-12-04 21:02:33 +03:00
void MainWidget : : set_path ( Gfx : : PathRole role , DeprecatedString value )
2022-05-11 14:56:16 +03:00
{
2022-05-10 18:10:15 +03:00
auto preview_palette = m_current_palette ;
2022-05-11 14:56:16 +03:00
preview_palette . set_path ( role , value ) ;
2022-05-10 18:10:15 +03:00
set_palette ( preview_palette ) ;
}
void MainWidget : : set_palette ( Gfx : : Palette palette )
{
m_current_palette = move ( palette ) ;
m_preview_widget - > set_preview_palette ( m_current_palette ) ;
2022-06-16 21:29:25 +03:00
m_theme_override_apply - > set_enabled ( true ) ;
2022-05-10 18:10:15 +03:00
window ( ) - > set_modified ( true ) ;
2022-05-11 14:56:16 +03:00
}
void MainWidget : : show_path_picker_dialog ( StringView property_display_name , GUI : : TextBox & path_input , PathPickerTarget path_picker_target )
{
bool open_folder = path_picker_target = = PathPickerTarget : : Folder ;
2022-12-04 21:02:33 +03:00
auto window_title = DeprecatedString : : formatted ( open_folder ? " Select {} folder " sv : " Select {} file " sv , property_display_name ) ;
2022-05-11 14:56:16 +03:00
auto target_path = path_input . text ( ) ;
2023-03-21 18:35:30 +03:00
if ( FileSystem : : exists ( target_path ) ) {
if ( ! FileSystem : : is_directory ( target_path ) )
2022-05-11 14:56:16 +03:00
target_path = LexicalPath : : dirname ( target_path ) ;
} else {
target_path = " /res/icons " ;
}
auto result = GUI : : FilePicker : : get_open_filepath ( window ( ) , window_title , target_path , open_folder ) ;
if ( ! result . has_value ( ) )
return ;
path_input . set_text ( * result ) ;
}
2023-02-09 05:02:46 +03:00
ErrorOr < void > MainWidget : : load_from_file ( String const & filename , NonnullOwnPtr < Core : : File > file )
2022-05-11 14:56:16 +03:00
{
2023-01-07 20:18:16 +03:00
auto config_file = TRY ( Core : : ConfigFile : : open ( filename . to_deprecated_string ( ) , move ( file ) ) ) ;
2022-12-06 19:26:13 +03:00
auto theme = TRY ( Gfx : : load_system_theme ( config_file ) ) ;
2022-05-10 18:10:15 +03:00
VERIFY ( theme . is_valid ( ) ) ;
auto new_palette = Gfx : : Palette ( Gfx : : PaletteImpl : : create_with_anonymous_buffer ( theme ) ) ;
set_palette ( move ( new_palette ) ) ;
2023-01-07 20:18:16 +03:00
set_path ( filename . to_deprecated_string ( ) ) ;
2022-05-11 14:56:16 +03:00
# define __ENUMERATE_ALIGNMENT_ROLE(role) \
if ( auto alignment_input = m_alignment_inputs [ to_underlying ( Gfx : : AlignmentRole : : role ) ] ) \
2022-05-10 18:10:15 +03:00
alignment_input - > set_selected_index ( m_alignment_model - > index_of ( m_current_palette . alignment ( Gfx : : AlignmentRole : : role ) ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:56:16 +03:00
ENUMERATE_ALIGNMENT_ROLES ( __ENUMERATE_ALIGNMENT_ROLE )
# undef __ENUMERATE_ALIGNMENT_ROLE
# define __ENUMERATE_COLOR_ROLE(role) \
if ( auto color_input = m_color_inputs [ to_underlying ( Gfx : : ColorRole : : role ) ] ) \
2022-05-10 18:10:15 +03:00
color_input - > set_color ( m_current_palette . color ( Gfx : : ColorRole : : role ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:56:16 +03:00
ENUMERATE_COLOR_ROLES ( __ENUMERATE_COLOR_ROLE )
# undef __ENUMERATE_COLOR_ROLE
# define __ENUMERATE_FLAG_ROLE(role) \
if ( auto flag_input = m_flag_inputs [ to_underlying ( Gfx : : FlagRole : : role ) ] ) \
2022-05-10 18:10:15 +03:00
flag_input - > set_checked ( m_current_palette . flag ( Gfx : : FlagRole : : role ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:56:16 +03:00
ENUMERATE_FLAG_ROLES ( __ENUMERATE_FLAG_ROLE )
# undef __ENUMERATE_FLAG_ROLE
# define __ENUMERATE_METRIC_ROLE(role) \
if ( auto metric_input = m_metric_inputs [ to_underlying ( Gfx : : MetricRole : : role ) ] ) \
2022-05-10 18:10:15 +03:00
metric_input - > set_value ( m_current_palette . metric ( Gfx : : MetricRole : : role ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:56:16 +03:00
ENUMERATE_METRIC_ROLES ( __ENUMERATE_METRIC_ROLE )
# undef __ENUMERATE_METRIC_ROLE
# define __ENUMERATE_PATH_ROLE(role) \
if ( auto path_input = m_path_inputs [ to_underlying ( Gfx : : PathRole : : role ) ] ) \
2022-05-10 18:10:15 +03:00
path_input - > set_text ( m_current_palette . path ( Gfx : : PathRole : : role ) , GUI : : AllowCallback : : No ) ;
2022-05-11 14:56:16 +03:00
ENUMERATE_PATH_ROLES ( __ENUMERATE_PATH_ROLE )
# undef __ENUMERATE_PATH_ROLE
2023-03-13 18:30:34 +03:00
m_last_modified_time = Duration : : now_monotonic ( ) ;
2022-05-11 14:56:16 +03:00
window ( ) - > set_modified ( false ) ;
2022-12-06 19:26:13 +03:00
return { } ;
2022-05-11 14:56:16 +03:00
}
2023-01-11 00:30:36 +03:00
void MainWidget : : drag_enter_event ( GUI : : DragEvent & event )
{
auto const & mime_types = event . mime_types ( ) ;
if ( mime_types . contains_slow ( " text/uri-list " ) )
event . accept ( ) ;
}
void MainWidget : : drop_event ( GUI : : DropEvent & event )
{
event . accept ( ) ;
window ( ) - > move_to_front ( ) ;
if ( event . mime_data ( ) . has_urls ( ) ) {
auto urls = event . mime_data ( ) . urls ( ) ;
if ( urls . is_empty ( ) )
return ;
if ( urls . size ( ) > 1 ) {
GUI : : MessageBox : : show ( window ( ) , " ThemeEditor can only open one file at a time! " sv , " One at a time please! " sv , GUI : : MessageBox : : Type : : Error ) ;
return ;
}
2023-01-11 00:41:12 +03:00
if ( request_close ( ) = = GUI : : Window : : CloseRequestDecision : : StayOpen )
return ;
2023-01-11 00:30:36 +03:00
2023-04-14 22:12:03 +03:00
auto response = FileSystemAccessClient : : Client : : the ( ) . request_file ( window ( ) , urls . first ( ) . serialize_path ( ) , Core : : File : : OpenMode : : Read ) ;
2023-01-11 00:30:36 +03:00
if ( response . is_error ( ) )
return ;
2023-01-11 00:48:33 +03:00
auto load_from_file_result = load_from_file ( response . value ( ) . filename ( ) , response . value ( ) . release_stream ( ) ) ;
if ( load_from_file_result . is_error ( ) )
GUI : : MessageBox : : show_error ( window ( ) , DeprecatedString : : formatted ( " Can't open file named {}: {} " , response . value ( ) . filename ( ) , load_from_file_result . error ( ) ) ) ;
2023-01-11 00:30:36 +03:00
}
}
2022-05-10 16:54:43 +03:00
}