2020-01-18 05:38:21 -03:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 05:38:21 -03:00
*/
2019-02-02 05:05:14 -02:00
# include "FontEditor.h"
2019-04-03 10:23:13 -03:00
# include "GlyphEditorWidget.h"
2019-06-07 06:48:03 -03:00
# include "GlyphMapWidget.h"
2021-04-10 14:47:23 -03:00
# include "NewFontDialog.h"
2019-09-04 17:50:40 -03:00
# include <AK/StringBuilder.h>
2021-05-20 08:00:04 -03:00
# include <AK/UnicodeUtils.h>
2021-04-06 13:04:21 -03:00
# include <Applications/FontEditor/FontEditorWindowGML.h>
2021-04-10 14:40:59 -03:00
# include <LibDesktop/Launcher.h>
2021-04-06 13:04:21 -03:00
# include <LibGUI/Action.h>
2021-04-10 14:40:59 -03:00
# include <LibGUI/Application.h>
2020-05-08 19:06:56 -03:00
# include <LibGUI/BoxLayout.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Button.h>
# include <LibGUI/CheckBox.h>
2021-04-10 14:40:59 -03:00
# include <LibGUI/Clipboard.h>
# include <LibGUI/ComboBox.h>
2021-04-06 13:04:21 -03:00
# include <LibGUI/FilePicker.h>
2021-04-10 14:40:59 -03:00
# include <LibGUI/FontPickerWeightModel.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/GroupBox.h>
2021-09-09 08:43:19 -03:00
# include <LibGUI/InputBox.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Label.h>
2021-04-10 14:40:59 -03:00
# include <LibGUI/Menu.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Menubar.h>
2020-05-08 19:06:56 -03:00
# include <LibGUI/MessageBox.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/Painter.h>
# include <LibGUI/SpinBox.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/Statusbar.h>
2020-02-06 16:33:02 -03:00
# include <LibGUI/TextBox.h>
2021-04-13 11:18:20 -03:00
# include <LibGUI/ToolbarContainer.h>
2020-05-08 19:06:56 -03:00
# include <LibGUI/Window.h>
2020-12-31 10:01:59 -03:00
# include <LibGfx/BitmapFont.h>
2020-05-08 19:06:56 -03:00
# include <LibGfx/Palette.h>
2021-05-20 08:00:04 -03:00
# include <LibGfx/TextDirection.h>
2019-04-09 22:51:03 -03:00
# include <stdlib.h>
2019-02-02 05:05:14 -02:00
2021-05-21 06:30:21 -03:00
static constexpr int s_pangram_count = 7 ;
static const char * pangrams [ s_pangram_count ] = {
" quick fox jumps nightly above wizard " ,
" five quacking zephyrs jolt my wax bed " ,
" pack my box with five dozen liquor jugs " ,
" quick brown fox jumps over the lazy dog " ,
" waxy and quivering jocks fumble the pizza " ,
" ~#:[@_1%]*{$2.3}/4^(5'6 \" )-&|7+8!=<9,0 \\ >?; " ,
" byxfjärmat föl gick på duvshowen "
} ;
2021-04-06 13:04:21 -03:00
static RefPtr < GUI : : Window > create_font_preview_window ( FontEditorWidget & editor )
2019-02-02 05:05:14 -02:00
{
2021-04-06 13:04:21 -03:00
auto window = GUI : : Window : : construct ( ) ;
window - > set_window_type ( GUI : : WindowType : : ToolWindow ) ;
window - > set_title ( " Font preview " ) ;
window - > resize ( 400 , 150 ) ;
window - > set_minimum_size ( 200 , 100 ) ;
window - > center_within ( * editor . window ( ) ) ;
auto & main_widget = window - > set_main_widget < GUI : : Widget > ( ) ;
main_widget . set_fill_with_background_color ( true ) ;
main_widget . set_layout < GUI : : VerticalBoxLayout > ( ) ;
Userland+LibGUI: Add shorthand versions of the Margins constructor
This allows for typing [8] instead of [8, 8, 8, 8] to specify the same
margin on all edges, for example. The constructors follow CSS' style of
specifying margins. The added constructors are:
- Margins(int all): Sets the same margin on all edges.
- Margins(int vertical, int horizontal): Sets the first argument to top
and bottom margins, and the second argument to left and right margins.
- Margins(int top, int vertical, int bottom): Sets the first argument to
the top margin, the second argument to the left and right margins,
and the third argument to the bottom margin.
2021-08-16 21:11:38 -03:00
main_widget . layout ( ) - > set_margins ( 2 ) ;
2021-04-06 13:04:21 -03:00
main_widget . layout ( ) - > set_spacing ( 4 ) ;
auto & preview_box = main_widget . add < GUI : : GroupBox > ( ) ;
preview_box . set_layout < GUI : : VerticalBoxLayout > ( ) ;
Userland+LibGUI: Add shorthand versions of the Margins constructor
This allows for typing [8] instead of [8, 8, 8, 8] to specify the same
margin on all edges, for example. The constructors follow CSS' style of
specifying margins. The added constructors are:
- Margins(int all): Sets the same margin on all edges.
- Margins(int vertical, int horizontal): Sets the first argument to top
and bottom margins, and the second argument to left and right margins.
- Margins(int top, int vertical, int bottom): Sets the first argument to
the top margin, the second argument to the left and right margins,
and the third argument to the bottom margin.
2021-08-16 21:11:38 -03:00
preview_box . layout ( ) - > set_margins ( 8 ) ;
2021-04-06 13:04:21 -03:00
auto & preview_label = preview_box . add < GUI : : Label > ( ) ;
preview_label . set_font ( editor . edited_font ( ) ) ;
editor . on_initialize = [ & ] {
preview_label . set_font ( editor . edited_font ( ) ) ;
2019-03-06 08:52:41 -03:00
} ;
2021-04-22 14:41:43 -03:00
auto & textbox_button_container = main_widget . add < GUI : : Widget > ( ) ;
textbox_button_container . set_layout < GUI : : HorizontalBoxLayout > ( ) ;
textbox_button_container . set_fixed_height ( 22 ) ;
auto & preview_textbox = textbox_button_container . add < GUI : : TextBox > ( ) ;
preview_textbox . set_text ( pangrams [ 0 ] ) ;
2021-04-10 14:40:59 -03:00
preview_textbox . set_placeholder ( " Preview text " ) ;
2020-10-24 12:03:39 -03:00
2021-04-06 13:04:21 -03:00
preview_textbox . on_change = [ & ] {
2021-04-10 14:40:59 -03:00
auto preview = String : : formatted ( " {} \n {} " ,
preview_textbox . text ( ) ,
preview_textbox . text ( ) . to_uppercase ( ) ) ;
preview_label . set_text ( preview ) ;
2020-10-24 12:03:39 -03:00
} ;
2021-04-22 14:41:43 -03:00
auto & reload_button = textbox_button_container . add < GUI : : Button > ( ) ;
2021-07-21 13:02:15 -03:00
reload_button . set_icon ( Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/reload.png " ) ) ;
2021-04-22 14:41:43 -03:00
reload_button . set_fixed_width ( 22 ) ;
AK+Everywhere: Disallow constructing Functions from incompatible types
Previously, AK::Function would accept _any_ callable type, and try to
call it when called, first with the given set of arguments, then with
zero arguments, and if all of those failed, it would simply not call the
function and **return a value-constructed Out type**.
This lead to many, many, many hard to debug situations when someone
forgot a `const` in their lambda argument types, and many cases of
people taking zero arguments in their lambdas to ignore them.
This commit reworks the Function interface to not include any such
surprising behaviour, if your function instance is not callable with
the declared argument set of the Function, it can simply not be
assigned to that Function instance, end of story.
2021-06-05 15:34:31 -03:00
reload_button . on_click = [ & ] ( auto ) {
2021-04-22 14:41:43 -03:00
static int i = 1 ;
2021-05-21 06:30:21 -03:00
if ( i > = s_pangram_count )
2021-04-22 14:41:43 -03:00
i = 0 ;
preview_textbox . set_text ( pangrams [ i ] ) ;
i + + ;
} ;
2021-04-06 13:04:21 -03:00
return window ;
}
FontEditorWidget : : FontEditorWidget ( const String & path , RefPtr < Gfx : : BitmapFont > & & edited_font )
{
load_from_gml ( font_editor_window_gml ) ;
2021-04-13 11:18:20 -03:00
auto & toolbar = * find_descendant_of_type_named < GUI : : Toolbar > ( " toolbar " ) ;
auto & statusbar = * find_descendant_of_type_named < GUI : : Statusbar > ( " statusbar " ) ;
2021-04-06 13:04:21 -03:00
auto & glyph_map_container = * find_descendant_of_type_named < GUI : : Widget > ( " glyph_map_container " ) ;
2021-04-22 15:15:21 -03:00
auto & move_glyph_button = * find_descendant_of_type_named < GUI : : Button > ( " move_glyph_button " ) ;
2021-04-06 13:04:21 -03:00
m_glyph_editor_container = * find_descendant_of_type_named < GUI : : Widget > ( " glyph_editor_container " ) ;
m_left_column_container = * find_descendant_of_type_named < GUI : : Widget > ( " left_column_container " ) ;
m_glyph_editor_width_spinbox = * find_descendant_of_type_named < GUI : : SpinBox > ( " glyph_editor_width_spinbox " ) ;
2021-04-18 15:12:52 -03:00
m_glyph_editor_present_checkbox = * find_descendant_of_type_named < GUI : : CheckBox > ( " glyph_editor_present_checkbox " ) ;
2021-04-06 13:04:21 -03:00
m_name_textbox = * find_descendant_of_type_named < GUI : : TextBox > ( " name_textbox " ) ;
m_family_textbox = * find_descendant_of_type_named < GUI : : TextBox > ( " family_textbox " ) ;
m_presentation_spinbox = * find_descendant_of_type_named < GUI : : SpinBox > ( " presentation_spinbox " ) ;
2021-04-10 14:40:59 -03:00
m_weight_combobox = * find_descendant_of_type_named < GUI : : ComboBox > ( " weight_combobox " ) ;
2021-04-06 13:04:21 -03:00
m_spacing_spinbox = * find_descendant_of_type_named < GUI : : SpinBox > ( " spacing_spinbox " ) ;
m_mean_line_spinbox = * find_descendant_of_type_named < GUI : : SpinBox > ( " mean_line_spinbox " ) ;
m_baseline_spinbox = * find_descendant_of_type_named < GUI : : SpinBox > ( " baseline_spinbox " ) ;
m_fixed_width_checkbox = * find_descendant_of_type_named < GUI : : CheckBox > ( " fixed_width_checkbox " ) ;
m_font_metadata_groupbox = * find_descendant_of_type_named < GUI : : GroupBox > ( " font_metadata_groupbox " ) ;
m_glyph_editor_widget = m_glyph_editor_container - > add < GlyphEditorWidget > ( ) ;
m_glyph_map_widget = glyph_map_container . add < GlyphMapWidget > ( ) ;
2021-04-22 14:53:17 -03:00
auto update_statusbar = [ & ] {
auto glyph = m_glyph_map_widget - > selected_glyph ( ) ;
StringBuilder builder ;
2021-05-20 08:00:04 -03:00
builder . appendff ( " U+{:04X} ( " , glyph ) ;
if ( AK : : UnicodeUtils : : is_unicode_control_code_point ( glyph ) ) {
builder . append ( AK : : UnicodeUtils : : get_unicode_control_code_point_alias ( glyph ) . value ( ) ) ;
} else if ( Gfx : : get_char_bidi_class ( glyph ) = = Gfx : : BidirectionalClass : : STRONG_RTL ) {
// FIXME: This is a necessary hack, as RTL text will mess up the painting of the statusbar text.
// For now, replace RTL glyphs with U+FFFD, the replacement character.
builder . append_code_point ( 0xFFFD ) ;
2021-04-22 14:53:17 -03:00
} else {
2021-05-20 08:00:04 -03:00
builder . append_code_point ( glyph ) ;
2021-04-22 14:53:17 -03:00
}
2021-05-20 08:00:04 -03:00
builder . append ( " ) " ) ;
if ( m_edited_font - > raw_glyph_width ( glyph ) > 0 )
builder . appendff ( " [{}x{}] " , m_edited_font - > raw_glyph_width ( glyph ) , m_edited_font - > glyph_height ( ) ) ;
2021-04-22 14:53:17 -03:00
statusbar . set_text ( builder . to_string ( ) ) ;
} ;
2020-05-08 19:06:56 -03:00
auto update_demo = [ & ] {
2021-04-06 13:04:21 -03:00
if ( m_font_preview_window )
m_font_preview_window - > update ( ) ;
2020-05-08 19:06:56 -03:00
} ;
2019-04-09 22:51:03 -03:00
2021-07-21 13:02:15 -03:00
m_new_action = GUI : : Action : : create ( " &New Font... " , { Mod_Ctrl , Key_N } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/filetype-font.png " ) , [ & ] ( auto & ) {
2021-08-26 18:25:02 -03:00
if ( window ( ) - > is_modified ( ) ) {
2021-04-26 10:11:14 -03:00
auto result = GUI : : MessageBox : : show ( window ( ) , " Save changes to the current font? " , " Unsaved changes " , GUI : : MessageBox : : Type : : Warning , GUI : : MessageBox : : InputType : : YesNoCancel ) ;
if ( result = = GUI : : Dialog : : ExecResult : : ExecYes ) {
m_save_action - > activate ( ) ;
2021-08-26 18:25:02 -03:00
if ( window ( ) - > is_modified ( ) )
2021-04-26 10:11:14 -03:00
return ;
}
if ( result = = GUI : : Dialog : : ExecResult : : ExecCancel )
return ;
}
2021-04-10 14:47:23 -03:00
auto new_font_wizard = NewFontDialog : : construct ( window ( ) ) ;
if ( new_font_wizard - > exec ( ) = = GUI : : Dialog : : ExecOK ) {
auto metadata = new_font_wizard - > new_font_metadata ( ) ;
2021-09-09 08:41:08 -03:00
RefPtr < Gfx : : BitmapFont > new_font = Gfx : : BitmapFont : : create ( metadata . glyph_height , metadata . glyph_width , metadata . is_fixed_width , 0x110000 ) ;
2021-04-10 14:47:23 -03:00
if ( ! new_font ) {
2021-08-26 18:25:02 -03:00
GUI : : MessageBox : : show ( window ( ) , " Failed to create new font. " , " Font Editor " , GUI : : MessageBox : : Type : : Error ) ;
2021-04-10 14:47:23 -03:00
return ;
}
new_font - > set_name ( metadata . name ) ;
new_font - > set_family ( metadata . family ) ;
new_font - > set_presentation_size ( metadata . presentation_size ) ;
new_font - > set_weight ( metadata . weight ) ;
new_font - > set_baseline ( metadata . baseline ) ;
new_font - > set_mean_line ( metadata . mean_line ) ;
2021-08-26 18:25:02 -03:00
window ( ) - > set_modified ( true ) ;
initialize ( { } , move ( new_font ) ) ;
2021-04-10 14:47:23 -03:00
}
} ) ;
2021-04-22 14:53:17 -03:00
m_new_action - > set_status_tip ( " Create a new font " ) ;
2021-04-10 14:47:23 -03:00
m_open_action = GUI : : CommonActions : : make_open_action ( [ & ] ( auto & ) {
2021-08-26 18:25:02 -03:00
if ( window ( ) - > is_modified ( ) ) {
2021-04-26 10:11:14 -03:00
auto result = GUI : : MessageBox : : show ( window ( ) , " Save changes to the current font? " , " Unsaved changes " , GUI : : MessageBox : : Type : : Warning , GUI : : MessageBox : : InputType : : YesNoCancel ) ;
if ( result = = GUI : : Dialog : : ExecResult : : ExecYes ) {
m_save_action - > activate ( ) ;
2021-08-26 18:25:02 -03:00
if ( window ( ) - > is_modified ( ) )
2021-04-26 10:11:14 -03:00
return ;
}
if ( result = = GUI : : Dialog : : ExecResult : : ExecCancel )
return ;
}
2021-04-06 13:04:21 -03:00
Optional < String > open_path = GUI : : FilePicker : : get_open_filepath ( window ( ) , { } , " /res/fonts/ " ) ;
if ( ! open_path . has_value ( ) )
return ;
2019-03-06 08:52:41 -03:00
2021-04-06 13:04:21 -03:00
auto bitmap_font = Gfx : : BitmapFont : : load_from_file ( open_path . value ( ) ) ;
if ( ! bitmap_font ) {
String message = String : : formatted ( " Couldn't load font: {} \n " , open_path . value ( ) ) ;
GUI : : MessageBox : : show ( window ( ) , message , " Font Editor " , GUI : : MessageBox : : Type : : Error ) ;
return ;
}
2021-09-09 08:41:08 -03:00
RefPtr < Gfx : : BitmapFont > new_font = static_ptr_cast < Gfx : : BitmapFont > ( bitmap_font - > unmasked_character_set ( ) ) ;
2021-04-06 13:04:21 -03:00
if ( ! new_font ) {
String message = String : : formatted ( " Couldn't load font: {} \n " , open_path . value ( ) ) ;
GUI : : MessageBox : : show ( window ( ) , message , " Font Editor " , GUI : : MessageBox : : Type : : Error ) ;
return ;
}
2021-08-26 18:25:02 -03:00
window ( ) - > set_modified ( false ) ;
2021-04-06 13:04:21 -03:00
initialize ( open_path . value ( ) , move ( new_font ) ) ;
} ) ;
2021-04-10 14:40:59 -03:00
m_save_action = GUI : : CommonActions : : make_save_action ( [ & ] ( auto & ) {
2021-08-26 18:25:02 -03:00
if ( m_path . is_empty ( ) )
2021-04-26 10:11:14 -03:00
m_save_as_action - > activate ( ) ;
2021-08-26 18:25:02 -03:00
else
save_as ( m_path ) ;
2021-04-06 13:04:21 -03:00
} ) ;
2021-04-10 14:40:59 -03:00
m_save_as_action = GUI : : CommonActions : : make_save_as_action ( [ & ] ( auto & ) {
2021-08-26 18:25:02 -03:00
LexicalPath lexical_path ( m_path . is_empty ( ) ? " Untitled.font " : m_path ) ;
2021-04-10 14:40:59 -03:00
Optional < String > save_path = GUI : : FilePicker : : get_save_filepath ( window ( ) , lexical_path . title ( ) , lexical_path . extension ( ) ) ;
if ( ! save_path . has_value ( ) )
return ;
2021-04-26 10:11:14 -03:00
save_as ( save_path . value ( ) ) ;
2021-04-10 14:40:59 -03:00
} ) ;
m_cut_action = GUI : : CommonActions : : make_cut_action ( [ & ] ( auto & ) {
2021-04-06 13:04:21 -03:00
m_glyph_editor_widget - > cut_glyph ( ) ;
} ) ;
2021-04-10 14:40:59 -03:00
m_copy_action = GUI : : CommonActions : : make_copy_action ( [ & ] ( auto & ) {
2021-04-06 13:04:21 -03:00
m_glyph_editor_widget - > copy_glyph ( ) ;
} ) ;
2021-04-10 14:40:59 -03:00
m_paste_action = GUI : : CommonActions : : make_paste_action ( [ & ] ( auto & ) {
2021-04-06 13:04:21 -03:00
m_glyph_editor_widget - > paste_glyph ( ) ;
m_glyph_map_widget - > update_glyph ( m_glyph_map_widget - > selected_glyph ( ) ) ;
} ) ;
2021-04-10 14:40:59 -03:00
m_paste_action - > set_enabled ( GUI : : Clipboard : : the ( ) . mime_type ( ) = = " glyph/x-fonteditor " ) ;
m_delete_action = GUI : : CommonActions : : make_delete_action ( [ & ] ( auto & ) {
2021-04-06 13:04:21 -03:00
m_edited_font - > set_glyph_width ( m_glyph_map_widget - > selected_glyph ( ) , m_edited_font - > max_glyph_width ( ) ) ;
m_glyph_editor_widget - > delete_glyph ( ) ;
m_glyph_map_widget - > update_glyph ( m_glyph_map_widget - > selected_glyph ( ) ) ;
2021-04-18 15:12:52 -03:00
auto glyph_width = m_edited_font - > raw_glyph_width ( m_glyph_map_widget - > selected_glyph ( ) ) ;
m_glyph_editor_width_spinbox - > set_value ( glyph_width ) ;
m_glyph_editor_present_checkbox - > set_checked ( glyph_width > 0 ) ;
2021-04-06 13:04:21 -03:00
} ) ;
2021-04-22 15:12:53 -03:00
m_undo_action = GUI : : CommonActions : : make_undo_action ( [ & ] ( auto & ) {
undo ( ) ;
} ) ;
m_undo_action - > set_enabled ( false ) ;
m_redo_action = GUI : : CommonActions : : make_redo_action ( [ & ] ( auto & ) {
redo ( ) ;
} ) ;
m_redo_action - > set_enabled ( false ) ;
2021-07-21 13:02:15 -03:00
m_open_preview_action = GUI : : Action : : create ( " &Preview Font " , { Mod_Ctrl , Key_P } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/find.png " ) , [ & ] ( auto & ) {
2021-04-06 13:04:21 -03:00
if ( ! m_font_preview_window )
m_font_preview_window = create_font_preview_window ( * this ) ;
m_font_preview_window - > show ( ) ;
m_font_preview_window - > move_to_front ( ) ;
} ) ;
2021-04-10 14:40:59 -03:00
m_open_preview_action - > set_checked ( false ) ;
2021-04-22 14:53:17 -03:00
m_open_preview_action - > set_status_tip ( " Preview the current font " ) ;
m_show_metadata_action = GUI : : Action : : create_checkable ( " Font &Metadata " , { Mod_Ctrl , Key_M } , [ & ] ( auto & action ) {
2021-04-10 14:40:59 -03:00
set_show_font_metadata ( action . is_checked ( ) ) ;
} ) ;
m_show_metadata_action - > set_checked ( true ) ;
2021-04-22 14:53:17 -03:00
m_show_metadata_action - > set_status_tip ( " Show or hide metadata about the current font " ) ;
2021-09-09 08:43:19 -03:00
m_go_to_glyph_action = GUI : : Action : : create ( " &Go to Glyph... " , { Mod_Ctrl , Key_G } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/go-to.png " ) , [ & ] ( auto & ) {
String input ;
if ( GUI : : InputBox : : show ( window ( ) , input , " Hexadecimal: " , " Go to Glyph " ) = = GUI : : InputBox : : ExecOK & & ! input . is_empty ( ) ) {
int code_point = strtoul ( & input [ 0 ] , nullptr , 16 ) ;
code_point = clamp ( code_point , 0x0000 , 0x10FFFF ) ;
m_glyph_map_widget - > set_focus ( true ) ;
m_glyph_map_widget - > set_selected_glyph ( code_point ) ;
m_glyph_map_widget - > scroll_to_glyph ( code_point ) ;
}
} ) ;
m_go_to_glyph_action - > set_status_tip ( " Go to the specified code point " ) ;
m_previous_glyph_action = GUI : : Action : : create ( " Pre&vious Glyph " , { Mod_Alt , Key_Left } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/go-back.png " ) , [ & ] ( auto & ) {
bool search_wrapped = false ;
for ( int i = m_glyph_map_widget - > selected_glyph ( ) - 1 ; ; - - i ) {
if ( i < 0 & & ! search_wrapped ) {
i = 0x10FFFF ;
search_wrapped = true ;
} else if ( i < 0 & & search_wrapped ) {
break ;
}
if ( m_edited_font - > raw_glyph_width ( i ) > 0 ) {
m_glyph_map_widget - > set_focus ( true ) ;
m_glyph_map_widget - > set_selected_glyph ( i ) ;
m_glyph_map_widget - > scroll_to_glyph ( i ) ;
break ;
}
}
} ) ;
m_previous_glyph_action - > set_status_tip ( " Seek the previous visible glyph " ) ;
m_next_glyph_action = GUI : : Action : : create ( " &Next Glyph " , { Mod_Alt , Key_Right } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/go-forward.png " ) , [ & ] ( auto & ) {
bool search_wrapped = false ;
for ( int i = m_glyph_map_widget - > selected_glyph ( ) + 1 ; ; + + i ) {
if ( i > 0x10FFFF & & ! search_wrapped ) {
i = 0 ;
search_wrapped = true ;
} else if ( i > 0x10FFFF & & search_wrapped ) {
break ;
}
if ( m_edited_font - > raw_glyph_width ( i ) > 0 ) {
m_glyph_map_widget - > set_focus ( true ) ;
m_glyph_map_widget - > set_selected_glyph ( i ) ;
m_glyph_map_widget - > scroll_to_glyph ( i ) ;
break ;
}
}
} ) ;
m_next_glyph_action - > set_status_tip ( " Seek the next visible glyph " ) ;
2021-04-06 13:04:21 -03:00
2021-04-10 14:40:59 -03:00
toolbar . add_action ( * m_new_action ) ;
toolbar . add_action ( * m_open_action ) ;
toolbar . add_action ( * m_save_action ) ;
2021-04-06 13:04:21 -03:00
toolbar . add_separator ( ) ;
2021-04-10 14:40:59 -03:00
toolbar . add_action ( * m_cut_action ) ;
toolbar . add_action ( * m_copy_action ) ;
toolbar . add_action ( * m_paste_action ) ;
toolbar . add_action ( * m_delete_action ) ;
2021-04-06 13:04:21 -03:00
toolbar . add_separator ( ) ;
2021-04-22 15:12:53 -03:00
toolbar . add_action ( * m_undo_action ) ;
toolbar . add_action ( * m_redo_action ) ;
toolbar . add_separator ( ) ;
2021-04-10 14:40:59 -03:00
toolbar . add_action ( * m_open_preview_action ) ;
2021-09-09 08:43:19 -03:00
toolbar . add_separator ( ) ;
toolbar . add_action ( * m_previous_glyph_action ) ;
toolbar . add_action ( * m_next_glyph_action ) ;
toolbar . add_action ( * m_go_to_glyph_action ) ;
2021-04-10 14:40:59 -03:00
2021-04-22 15:04:19 -03:00
m_scale_five_action = GUI : : Action : : create_checkable ( " 500% " , { Mod_Ctrl , Key_1 } , [ & ] ( auto & ) {
m_glyph_editor_widget - > set_scale ( 5 ) ;
m_glyph_editor_container - > set_fixed_size ( m_glyph_editor_widget - > preferred_width ( ) , m_glyph_editor_widget - > preferred_height ( ) ) ;
m_left_column_container - > set_fixed_width ( m_glyph_editor_widget - > preferred_width ( ) ) ;
} ) ;
m_scale_five_action - > set_checked ( false ) ;
m_scale_five_action - > set_status_tip ( " Scale the editor in proportion to the current font " ) ;
m_scale_ten_action = GUI : : Action : : create_checkable ( " 1000% " , { Mod_Ctrl , Key_2 } , [ & ] ( auto & ) {
m_glyph_editor_widget - > set_scale ( 10 ) ;
m_glyph_editor_container - > set_fixed_size ( m_glyph_editor_widget - > preferred_width ( ) , m_glyph_editor_widget - > preferred_height ( ) ) ;
m_left_column_container - > set_fixed_width ( m_glyph_editor_widget - > preferred_width ( ) ) ;
} ) ;
m_scale_ten_action - > set_checked ( true ) ;
m_scale_ten_action - > set_status_tip ( " Scale the editor in proportion to the current font " ) ;
m_scale_fifteen_action = GUI : : Action : : create_checkable ( " 1500% " , { Mod_Ctrl , Key_3 } , [ & ] ( auto & ) {
m_glyph_editor_widget - > set_scale ( 15 ) ;
m_glyph_editor_container - > set_fixed_size ( m_glyph_editor_widget - > preferred_width ( ) , m_glyph_editor_widget - > preferred_height ( ) ) ;
m_left_column_container - > set_fixed_width ( m_glyph_editor_widget - > preferred_width ( ) ) ;
} ) ;
m_scale_fifteen_action - > set_checked ( false ) ;
m_scale_fifteen_action - > set_status_tip ( " Scale the editor in proportion to the current font " ) ;
m_glyph_editor_scale_actions . add_action ( * m_scale_five_action ) ;
m_glyph_editor_scale_actions . add_action ( * m_scale_ten_action ) ;
m_glyph_editor_scale_actions . add_action ( * m_scale_fifteen_action ) ;
m_glyph_editor_scale_actions . set_exclusive ( true ) ;
AK+Everywhere: Disallow constructing Functions from incompatible types
Previously, AK::Function would accept _any_ callable type, and try to
call it when called, first with the given set of arguments, then with
zero arguments, and if all of those failed, it would simply not call the
function and **return a value-constructed Out type**.
This lead to many, many, many hard to debug situations when someone
forgot a `const` in their lambda argument types, and many cases of
people taking zero arguments in their lambdas to ignore them.
This commit reworks the Function interface to not include any such
surprising behaviour, if your function instance is not callable with
the declared argument set of the Function, it can simply not be
assigned to that Function instance, end of story.
2021-06-05 15:34:31 -03:00
move_glyph_button . on_click = [ & ] ( auto ) {
2021-04-22 15:15:21 -03:00
if ( move_glyph_button . is_checked ( ) )
m_glyph_editor_widget - > set_mode ( GlyphEditorWidget : : Move ) ;
else
m_glyph_editor_widget - > set_mode ( GlyphEditorWidget : : Paint ) ;
} ;
move_glyph_button . set_checkable ( true ) ;
2021-07-21 13:02:15 -03:00
move_glyph_button . set_icon ( Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/selection-move.png " ) ) ;
2021-04-22 15:15:21 -03:00
2021-04-10 14:40:59 -03:00
GUI : : Clipboard : : the ( ) . on_change = [ & ] ( const String & data_type ) {
m_paste_action - > set_enabled ( data_type = = " glyph/x-fonteditor " ) ;
} ;
2019-02-02 05:05:14 -02:00
2021-04-10 14:40:59 -03:00
m_glyph_editor_widget - > on_glyph_altered = [ this , update_demo ] ( int glyph ) {
2019-04-06 10:28:06 -03:00
m_glyph_map_widget - > update_glyph ( glyph ) ;
2019-03-06 08:52:41 -03:00
update_demo ( ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2019-03-06 08:52:41 -03:00
} ;
2021-08-26 13:01:52 -03:00
m_glyph_editor_widget - > on_undo_event = [ this ] {
2021-04-22 15:12:53 -03:00
m_undo_stack - > push ( make < GlyphUndoCommand > ( * m_undo_glyph ) ) ;
} ;
m_glyph_map_widget - > on_glyph_selected = [ & , update_statusbar ] ( int glyph ) {
if ( m_undo_glyph )
m_undo_glyph - > set_code_point ( glyph ) ;
2019-02-02 05:05:14 -02:00
m_glyph_editor_widget - > set_glyph ( glyph ) ;
2021-04-18 15:12:52 -03:00
auto glyph_width = m_edited_font - > raw_glyph_width ( m_glyph_map_widget - > selected_glyph ( ) ) ;
m_glyph_editor_width_spinbox - > set_value ( glyph_width ) ;
m_glyph_editor_present_checkbox - > set_checked ( glyph_width > 0 ) ;
2021-04-22 14:53:17 -03:00
update_statusbar ( ) ;
2021-04-06 13:04:21 -03:00
} ;
m_name_textbox - > on_change = [ & ] {
m_edited_font - > set_name ( m_name_textbox - > text ( ) ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2021-04-06 13:04:21 -03:00
} ;
m_family_textbox - > on_change = [ & ] {
m_edited_font - > set_family ( m_family_textbox - > text ( ) ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2019-02-02 05:05:14 -02:00
} ;
2021-04-06 13:04:21 -03:00
m_fixed_width_checkbox - > on_checked = [ & , update_demo ] ( bool checked ) {
2019-05-24 12:11:42 -03:00
m_edited_font - > set_fixed_width ( checked ) ;
2021-04-18 15:12:52 -03:00
auto glyph_width = m_edited_font - > raw_glyph_width ( m_glyph_map_widget - > selected_glyph ( ) ) ;
m_glyph_editor_width_spinbox - > set_visible ( ! checked ) ;
m_glyph_editor_width_spinbox - > set_value ( glyph_width ) ;
m_glyph_editor_present_checkbox - > set_visible ( checked ) ;
m_glyph_editor_present_checkbox - > set_checked ( glyph_width > 0 ) ;
2019-03-06 08:52:41 -03:00
m_glyph_editor_widget - > update ( ) ;
update_demo ( ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2019-03-06 08:52:41 -03:00
} ;
2021-04-22 14:53:17 -03:00
m_glyph_editor_width_spinbox - > on_change = [ this , update_demo , update_statusbar ] ( int value ) {
2021-04-26 10:11:14 -03:00
if ( m_edited_font - > raw_glyph_width ( m_glyph_map_widget - > selected_glyph ( ) ) = = value )
return ;
2019-04-09 11:29:00 -03:00
m_edited_font - > set_glyph_width ( m_glyph_map_widget - > selected_glyph ( ) , value ) ;
m_glyph_editor_widget - > update ( ) ;
2019-04-09 23:02:20 -03:00
m_glyph_map_widget - > update_glyph ( m_glyph_map_widget - > selected_glyph ( ) ) ;
2019-04-09 11:29:00 -03:00
update_demo ( ) ;
2021-04-22 14:53:17 -03:00
update_statusbar ( ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2019-03-06 08:52:41 -03:00
} ;
2021-04-22 14:53:17 -03:00
m_glyph_editor_present_checkbox - > on_checked = [ this , update_demo , update_statusbar ] ( bool checked ) {
2021-04-26 10:11:14 -03:00
if ( ! m_edited_font - > is_fixed_width ( )
| | m_edited_font - > raw_glyph_width ( m_glyph_map_widget - > selected_glyph ( ) ) = = checked
| | ( m_edited_font - > raw_glyph_width ( m_glyph_map_widget - > selected_glyph ( ) ) & & checked ) )
2021-04-22 14:53:17 -03:00
return ;
2021-04-18 15:12:52 -03:00
m_edited_font - > set_glyph_width ( m_glyph_map_widget - > selected_glyph ( ) , checked ? m_edited_font - > glyph_fixed_width ( ) : 0 ) ;
m_glyph_editor_widget - > update ( ) ;
m_glyph_map_widget - > update_glyph ( m_glyph_map_widget - > selected_glyph ( ) ) ;
update_demo ( ) ;
2021-04-22 14:53:17 -03:00
update_statusbar ( ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2021-04-18 15:12:52 -03:00
} ;
AK+Everywhere: Disallow constructing Functions from incompatible types
Previously, AK::Function would accept _any_ callable type, and try to
call it when called, first with the given set of arguments, then with
zero arguments, and if all of those failed, it would simply not call the
function and **return a value-constructed Out type**.
This lead to many, many, many hard to debug situations when someone
forgot a `const` in their lambda argument types, and many cases of
people taking zero arguments in their lambdas to ignore them.
This commit reworks the Function interface to not include any such
surprising behaviour, if your function instance is not callable with
the declared argument set of the Function, it can simply not be
assigned to that Function instance, end of story.
2021-06-05 15:34:31 -03:00
m_weight_combobox - > on_change = [ this ] ( auto & , auto & ) {
2021-04-10 14:40:59 -03:00
m_edited_font - > set_weight ( GUI : : name_to_weight ( m_weight_combobox - > text ( ) ) ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2021-04-10 14:40:59 -03:00
} ;
2021-04-06 13:04:21 -03:00
m_presentation_spinbox - > on_change = [ this , update_demo ] ( int value ) {
2020-10-24 12:03:39 -03:00
m_edited_font - > set_presentation_size ( value ) ;
update_demo ( ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2020-10-24 12:03:39 -03:00
} ;
2021-04-06 13:04:21 -03:00
m_spacing_spinbox - > on_change = [ this , update_demo ] ( int value ) {
2019-12-30 07:40:30 -03:00
m_edited_font - > set_glyph_spacing ( value ) ;
update_demo ( ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2019-12-30 07:40:30 -03:00
} ;
2021-04-06 13:04:21 -03:00
m_baseline_spinbox - > on_change = [ this , update_demo ] ( int value ) {
2020-09-19 13:01:32 -03:00
m_edited_font - > set_baseline ( value ) ;
2020-09-20 16:45:21 -03:00
m_glyph_editor_widget - > update ( ) ;
2020-09-19 13:01:32 -03:00
update_demo ( ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2020-09-19 13:01:32 -03:00
} ;
2021-04-06 13:04:21 -03:00
m_mean_line_spinbox - > on_change = [ this , update_demo ] ( int value ) {
2020-10-05 12:19:37 -03:00
m_edited_font - > set_mean_line ( value ) ;
m_glyph_editor_widget - > update ( ) ;
update_demo ( ) ;
2021-04-26 10:11:14 -03:00
did_modify_font ( ) ;
2020-10-05 12:19:37 -03:00
} ;
2021-04-22 14:53:17 -03:00
GUI : : Application : : the ( ) - > on_action_enter = [ & statusbar ] ( GUI : : Action & action ) {
auto text = action . status_tip ( ) ;
if ( text . is_empty ( ) )
text = Gfx : : parse_ampersand_string ( action . text ( ) ) ;
statusbar . set_override_text ( move ( text ) ) ;
} ;
GUI : : Application : : the ( ) - > on_action_leave = [ & statusbar ] ( GUI : : Action & ) {
statusbar . set_override_text ( { } ) ;
} ;
2021-04-06 13:04:21 -03:00
initialize ( path , move ( edited_font ) ) ;
2019-02-02 05:05:14 -02:00
}
FontEditorWidget : : ~ FontEditorWidget ( )
{
}
2020-07-10 22:20:39 -03:00
2021-04-06 13:04:21 -03:00
void FontEditorWidget : : initialize ( const String & path , RefPtr < Gfx : : BitmapFont > & & edited_font )
{
if ( m_edited_font = = edited_font )
return ;
m_path = path ;
m_edited_font = edited_font ;
2021-04-07 13:05:02 -03:00
m_glyph_map_widget - > initialize ( * m_edited_font ) ;
2021-04-06 13:04:21 -03:00
m_glyph_editor_widget - > initialize ( * m_edited_font ) ;
2021-04-07 13:05:02 -03:00
2021-04-06 13:04:21 -03:00
m_glyph_editor_container - > set_fixed_size ( m_glyph_editor_widget - > preferred_width ( ) , m_glyph_editor_widget - > preferred_height ( ) ) ;
m_left_column_container - > set_fixed_width ( m_glyph_editor_widget - > preferred_width ( ) ) ;
2021-04-18 15:12:52 -03:00
m_glyph_editor_width_spinbox - > set_visible ( ! m_edited_font - > is_fixed_width ( ) ) ;
2021-04-06 13:04:21 -03:00
m_glyph_editor_width_spinbox - > set_max ( m_edited_font - > max_glyph_width ( ) ) ;
2021-04-18 15:12:52 -03:00
m_glyph_editor_present_checkbox - > set_visible ( m_edited_font - > is_fixed_width ( ) ) ;
2021-04-06 13:04:21 -03:00
m_name_textbox - > set_text ( m_edited_font - > name ( ) ) ;
m_family_textbox - > set_text ( m_edited_font - > family ( ) ) ;
m_presentation_spinbox - > set_value ( m_edited_font - > presentation_size ( ) ) ;
m_spacing_spinbox - > set_value ( m_edited_font - > glyph_spacing ( ) ) ;
2021-04-22 15:20:57 -03:00
m_mean_line_spinbox - > set_range ( 0 , max ( m_edited_font - > glyph_height ( ) - 2 , 0 ) , false ) ;
m_baseline_spinbox - > set_range ( 0 , max ( m_edited_font - > glyph_height ( ) - 2 , 0 ) , false ) ;
2021-04-06 13:04:21 -03:00
m_mean_line_spinbox - > set_value ( m_edited_font - > mean_line ( ) ) ;
m_baseline_spinbox - > set_value ( m_edited_font - > baseline ( ) ) ;
2021-04-10 14:40:59 -03:00
m_font_weight_list . clear ( ) ;
for ( auto & it : GUI : : font_weight_names )
m_font_weight_list . append ( it . name ) ;
m_weight_combobox - > set_model ( * GUI : : ItemListModel < String > : : create ( m_font_weight_list ) ) ;
int i = 0 ;
for ( auto it : GUI : : font_weight_names ) {
if ( it . weight = = m_edited_font - > weight ( ) ) {
m_weight_combobox - > set_selected_index ( i ) ;
break ;
}
i + + ;
}
2021-04-06 13:04:21 -03:00
m_fixed_width_checkbox - > set_checked ( m_edited_font - > is_fixed_width ( ) ) ;
2021-04-07 13:05:02 -03:00
m_glyph_map_widget - > set_selected_glyph ( ' A ' ) ;
2021-08-30 15:12:48 -03:00
deferred_invoke ( [ this ] {
2021-04-10 14:40:59 -03:00
m_glyph_map_widget - > set_focus ( true ) ;
m_glyph_map_widget - > scroll_to_glyph ( m_glyph_map_widget - > selected_glyph ( ) ) ;
2021-08-26 18:25:02 -03:00
window ( ) - > set_modified ( false ) ;
2021-04-26 10:11:14 -03:00
update_title ( ) ;
2021-04-10 14:40:59 -03:00
} ) ;
2021-04-07 13:05:02 -03:00
2021-04-22 15:12:53 -03:00
m_undo_stack = make < GUI : : UndoStack > ( ) ;
2021-04-23 11:46:57 -03:00
m_undo_glyph = adopt_ref ( * new UndoGlyph ( m_glyph_map_widget - > selected_glyph ( ) , * m_edited_font ) ) ;
2021-08-26 18:25:02 -03:00
m_undo_stack - > on_state_change = [ this ] {
m_undo_action - > set_enabled ( m_undo_stack - > can_undo ( ) ) ;
m_redo_action - > set_enabled ( m_undo_stack - > can_redo ( ) ) ;
did_modify_font ( ) ;
} ;
2021-04-22 15:12:53 -03:00
2021-04-06 13:04:21 -03:00
if ( on_initialize )
on_initialize ( ) ;
}
2021-07-21 16:21:03 -03:00
void FontEditorWidget : : initialize_menubar ( GUI : : Window & window )
2021-04-10 14:40:59 -03:00
{
2021-07-21 16:21:03 -03:00
auto & file_menu = window . add_menu ( " &File " ) ;
2021-05-01 05:45:39 -03:00
file_menu . add_action ( * m_new_action ) ;
file_menu . add_action ( * m_open_action ) ;
file_menu . add_action ( * m_save_action ) ;
file_menu . add_action ( * m_save_as_action ) ;
file_menu . add_separator ( ) ;
file_menu . add_action ( GUI : : CommonActions : : make_quit_action ( [ this ] ( auto & ) {
2021-04-26 10:11:14 -03:00
if ( ! request_close ( ) )
return ;
2021-04-10 14:40:59 -03:00
GUI : : Application : : the ( ) - > quit ( ) ;
} ) ) ;
2021-07-21 16:21:03 -03:00
auto & edit_menu = window . add_menu ( " &Edit " ) ;
2021-04-22 15:12:53 -03:00
edit_menu . add_action ( * m_undo_action ) ;
edit_menu . add_action ( * m_redo_action ) ;
edit_menu . add_separator ( ) ;
2021-04-10 14:40:59 -03:00
edit_menu . add_action ( * m_cut_action ) ;
edit_menu . add_action ( * m_copy_action ) ;
edit_menu . add_action ( * m_paste_action ) ;
edit_menu . add_action ( * m_delete_action ) ;
2021-09-09 08:43:19 -03:00
edit_menu . add_separator ( ) ;
edit_menu . add_action ( * m_previous_glyph_action ) ;
edit_menu . add_action ( * m_next_glyph_action ) ;
edit_menu . add_action ( * m_go_to_glyph_action ) ;
2021-04-10 14:40:59 -03:00
2021-07-21 16:21:03 -03:00
auto & view_menu = window . add_menu ( " &View " ) ;
2021-04-10 14:40:59 -03:00
view_menu . add_action ( * m_open_preview_action ) ;
view_menu . add_separator ( ) ;
view_menu . add_action ( * m_show_metadata_action ) ;
2021-04-22 15:04:19 -03:00
view_menu . add_separator ( ) ;
auto & scale_menu = view_menu . add_submenu ( " &Scale " ) ;
scale_menu . add_action ( * m_scale_five_action ) ;
scale_menu . add_action ( * m_scale_ten_action ) ;
scale_menu . add_action ( * m_scale_fifteen_action ) ;
2021-04-10 14:40:59 -03:00
2021-07-21 16:21:03 -03:00
auto & help_menu = window . add_menu ( " &Help " ) ;
2021-04-10 14:40:59 -03:00
help_menu . add_action ( GUI : : CommonActions : : make_help_action ( [ ] ( auto & ) {
Desktop : : Launcher : : open ( URL : : create_with_file_protocol ( " /usr/share/man/man1/FontEditor.md " ) , " /bin/Help " ) ;
} ) ) ;
2021-07-21 16:21:03 -03:00
help_menu . add_action ( GUI : : CommonActions : : make_about_action ( " Font Editor " , GUI : : Icon : : default_icon ( " app-font-editor " ) , & window ) ) ;
2021-04-10 14:40:59 -03:00
}
2020-07-10 22:20:39 -03:00
bool FontEditorWidget : : save_as ( const String & path )
{
2021-09-09 08:41:08 -03:00
auto saved_font = m_edited_font - > masked_character_set ( ) ;
auto ret_val = saved_font - > write_to_file ( path ) ;
2020-07-10 22:20:39 -03:00
if ( ! ret_val ) {
2020-07-15 23:45:11 -03:00
GUI : : MessageBox : : show ( window ( ) , " The font file could not be saved. " , " Save failed " , GUI : : MessageBox : : Type : : Error ) ;
2020-07-10 22:20:39 -03:00
return false ;
}
m_path = path ;
2021-08-26 18:25:02 -03:00
window ( ) - > set_modified ( false ) ;
2021-04-26 10:11:14 -03:00
update_title ( ) ;
2020-07-10 22:20:39 -03:00
return true ;
}
2021-04-06 13:04:21 -03:00
void FontEditorWidget : : set_show_font_metadata ( bool show )
{
if ( m_font_metadata = = show )
return ;
m_font_metadata = show ;
m_font_metadata_groupbox - > set_visible ( m_font_metadata ) ;
}
2021-04-22 15:12:53 -03:00
void FontEditorWidget : : undo ( )
{
if ( ! m_undo_stack - > can_undo ( ) )
return ;
m_undo_stack - > undo ( ) ;
m_glyph_editor_widget - > update ( ) ;
m_glyph_map_widget - > update ( ) ;
if ( m_font_preview_window )
m_font_preview_window - > update ( ) ;
}
void FontEditorWidget : : redo ( )
{
if ( ! m_undo_stack - > can_redo ( ) )
return ;
m_undo_stack - > redo ( ) ;
m_glyph_editor_widget - > update ( ) ;
m_glyph_map_widget - > update ( ) ;
if ( m_font_preview_window )
m_font_preview_window - > update ( ) ;
}
2021-04-26 10:11:14 -03:00
bool FontEditorWidget : : request_close ( )
{
2021-08-26 18:25:02 -03:00
if ( ! window ( ) - > is_modified ( ) )
2021-04-26 10:11:14 -03:00
return true ;
auto result = GUI : : MessageBox : : show ( window ( ) , " Save changes to the current font? " , " Unsaved changes " , GUI : : MessageBox : : Type : : Warning , GUI : : MessageBox : : InputType : : YesNoCancel ) ;
if ( result = = GUI : : MessageBox : : ExecYes ) {
m_save_action - > activate ( ) ;
2021-08-26 18:25:02 -03:00
if ( ! window ( ) - > is_modified ( ) )
2021-04-26 10:11:14 -03:00
return true ;
}
if ( result = = GUI : : MessageBox : : ExecNo )
return true ;
return false ;
}
void FontEditorWidget : : update_title ( )
{
StringBuilder title ;
2021-08-26 18:25:02 -03:00
if ( m_path . is_empty ( ) )
title . append ( " Untitled " ) ;
else
title . append ( m_path ) ;
title . append ( " [*] - Font Editor " ) ;
window ( ) - > set_title ( title . to_string ( ) ) ;
2021-04-26 10:11:14 -03:00
}
void FontEditorWidget : : did_modify_font ( )
{
2021-08-26 18:25:02 -03:00
if ( ! window ( ) | | window ( ) - > is_modified ( ) )
2021-04-26 10:11:14 -03:00
return ;
2021-08-26 18:25:02 -03:00
window ( ) - > set_modified ( true ) ;
2021-04-26 10:11:14 -03:00
update_title ( ) ;
2021-04-22 15:12:53 -03:00
}