2020-01-18 09:38:21 +01:00
/*
2023-02-20 19:03:44 +01:00
* Copyright ( c ) 2018 - 2023 , Andreas Kling < kling @ serenityos . org >
2022-02-26 10:50:04 -07:00
* Copyright ( c ) 2022 , the SerenityOS developers .
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2019-01-20 04:49:48 +01:00
# include <AK/Assertions.h>
2022-01-09 23:35:42 +01:00
# include <AK/Debug.h>
2022-02-04 17:19:22 +01:00
# include <AK/IterationDecision.h>
2019-08-18 20:39:46 +02:00
# include <AK/JsonObject.h>
2022-02-04 17:19:22 +01:00
# include <AK/NonnullRefPtr.h>
# include <AK/RefPtr.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/Action.h>
# include <LibGUI/Application.h>
2020-09-14 12:46:53 +02:00
# include <LibGUI/BoxLayout.h>
2022-02-25 12:39:33 +02:00
# include <LibGUI/ConnectionToWindowServer.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/Event.h>
2022-02-04 17:19:22 +01:00
# include <LibGUI/GML/AST.h>
2022-02-02 18:11:29 +01:00
# include <LibGUI/GML/Parser.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/Layout.h>
# include <LibGUI/Menu.h>
# include <LibGUI/Painter.h>
2022-03-18 22:57:05 +01:00
# include <LibGUI/TabWidget.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/Widget.h>
# include <LibGUI/Window.h>
2020-02-13 21:43:32 +01:00
# include <LibGfx/Bitmap.h>
2022-04-09 09:28:38 +02:00
# include <LibGfx/Font/Font.h>
# include <LibGfx/Font/FontDatabase.h>
2020-02-13 21:43:32 +01:00
# include <LibGfx/Palette.h>
2021-06-28 13:30:28 +02:00
# include <LibGfx/SystemTheme.h>
2019-12-24 20:57:54 +01:00
# include <unistd.h>
2019-11-10 10:58:03 +01:00
2023-08-06 15:14:59 +02:00
REGISTER_GUI_OBJECT ( GUI , Widget )
2020-02-02 15:07:41 +01:00
2021-01-02 16:30:13 -07:00
namespace GUI {
2020-02-23 12:07:13 +01:00
Widget : : Widget ( )
2023-08-06 15:14:59 +02:00
: m_background_role ( Gfx : : ColorRole : : Window )
2020-02-14 23:53:11 +01:00
, m_foreground_role ( Gfx : : ColorRole : : WindowText )
2020-12-29 18:25:13 +01:00
, m_font ( Gfx : : FontDatabase : : default_font ( ) )
2020-07-04 16:52:01 +02:00
, m_palette ( Application : : the ( ) - > palette ( ) . impl ( ) )
2019-01-20 04:49:48 +01:00
{
2023-08-06 15:14:59 +02:00
REGISTER_READONLY_STRING_PROPERTY ( " class_name " , class_name ) ;
REGISTER_DEPRECATED_STRING_PROPERTY ( " name " , name , set_name ) ;
register_property (
2023-11-05 19:29:02 -05:00
" address " sv , [ this ] { return FlatPtr ( this ) ; } ,
nullptr , nullptr ) ;
2023-08-06 15:14:59 +02:00
register_property (
2023-11-05 19:29:02 -05:00
" parent " sv , [ this ] { return FlatPtr ( this - > parent ( ) ) ; } ,
nullptr , nullptr ) ;
2023-08-06 15:14:59 +02:00
2020-09-15 21:33:37 +02:00
REGISTER_RECT_PROPERTY ( " relative_rect " , relative_rect , set_relative_rect ) ;
REGISTER_BOOL_PROPERTY ( " fill_with_background_color " , fill_with_background_color , set_fill_with_background_color ) ;
REGISTER_BOOL_PROPERTY ( " visible " , is_visible , set_visible ) ;
REGISTER_BOOL_PROPERTY ( " focused " , is_focused , set_focus ) ;
REGISTER_BOOL_PROPERTY ( " enabled " , is_enabled , set_enabled ) ;
2023-08-01 20:28:44 +01:00
REGISTER_STRING_PROPERTY ( " tooltip " , tooltip , set_tooltip ) ;
2020-12-07 19:46:34 +01:00
2022-06-12 22:03:45 +02:00
REGISTER_UI_SIZE_PROPERTY ( " min_size " , min_size , set_min_size ) ;
2022-05-16 18:40:14 +02:00
REGISTER_READONLY_UI_SIZE_PROPERTY ( " effective_min_size " , effective_min_size ) ;
2022-06-12 22:03:45 +02:00
REGISTER_UI_SIZE_PROPERTY ( " max_size " , max_size , set_max_size ) ;
2022-06-12 22:16:39 +02:00
REGISTER_UI_SIZE_PROPERTY ( " preferred_size " , preferred_size , set_preferred_size ) ;
2022-05-16 18:40:14 +02:00
REGISTER_READONLY_UI_SIZE_PROPERTY ( " effective_preferred_size " , effective_preferred_size ) ;
2021-01-02 17:27:15 -05:00
REGISTER_INT_PROPERTY ( " width " , width , set_width ) ;
2022-06-12 22:03:45 +02:00
REGISTER_UI_DIMENSION_PROPERTY ( " min_width " , min_width , set_min_width ) ;
REGISTER_UI_DIMENSION_PROPERTY ( " max_width " , max_width , set_max_width ) ;
2022-06-12 22:16:39 +02:00
REGISTER_UI_DIMENSION_PROPERTY ( " preferred_width " , preferred_width , set_preferred_width ) ;
2021-01-02 17:27:15 -05:00
REGISTER_INT_PROPERTY ( " height " , height , set_height ) ;
2022-06-12 22:03:45 +02:00
REGISTER_UI_DIMENSION_PROPERTY ( " min_height " , min_height , set_min_height ) ;
REGISTER_UI_DIMENSION_PROPERTY ( " max_height " , max_height , set_max_height ) ;
2022-06-12 22:16:39 +02:00
REGISTER_UI_DIMENSION_PROPERTY ( " preferred_height " , preferred_height , set_preferred_height ) ;
2020-12-29 18:22:51 +01:00
2020-12-29 18:46:42 +01:00
REGISTER_INT_PROPERTY ( " fixed_width " , dummy_fixed_width , set_fixed_width ) ;
REGISTER_INT_PROPERTY ( " fixed_height " , dummy_fixed_height , set_fixed_height ) ;
REGISTER_SIZE_PROPERTY ( " fixed_size " , dummy_fixed_size , set_fixed_size ) ;
2021-01-04 18:17:14 +01:00
REGISTER_BOOL_PROPERTY ( " shrink_to_fit " , is_shrink_to_fit , set_shrink_to_fit ) ;
2021-01-02 17:27:15 -05:00
REGISTER_INT_PROPERTY ( " x " , x , set_x ) ;
REGISTER_INT_PROPERTY ( " y " , y , set_y ) ;
2023-08-01 20:28:44 +01:00
REGISTER_STRING_PROPERTY ( " font " , font_family , set_font_family ) ;
2021-03-22 19:51:06 +00:00
REGISTER_INT_PROPERTY ( " font_size " , m_font - > presentation_size , set_font_size ) ;
REGISTER_FONT_WEIGHT_PROPERTY ( " font_weight " , m_font - > weight , set_font_weight ) ;
2023-03-10 18:40:34 +01:00
REGISTER_STRING_PROPERTY ( " title " , title , set_title ) ;
2022-03-18 22:54:30 +01:00
2023-05-26 14:07:41 +02:00
REGISTER_BOOL_PROPERTY ( " font_fixed_width " , is_font_fixed_width , set_font_fixed_width )
2021-03-22 19:51:06 +00:00
register_property (
2023-11-05 19:29:02 -05:00
" font_type " sv , [ this ] { return m_font - > is_fixed_width ( ) ? " FixedWidth " : " Normal " ; } ,
[ ] ( JsonValue const & value ) - > ErrorOr < bool > {
if ( value . is_string ( ) ) {
auto string = value . as_string ( ) ;
if ( string = = " FixedWidth " )
return true ;
if ( string = = " Normal " )
return false ;
2021-03-22 19:51:06 +00:00
}
2023-11-05 19:29:02 -05:00
return Error : : from_string_literal ( " \" FixedWidth \" or \" Normal \" is expected " ) ;
} ,
[ this ] ( auto const & value ) { return set_font_fixed_width ( value ) ; } ) ;
2021-03-22 19:51:06 +00:00
2023-11-05 19:29:02 -05:00
REGISTER_ENUM_PROPERTY ( " focus_policy " , focus_policy , set_focus_policy , GUI : : FocusPolicy ,
{ GUI : : FocusPolicy : : ClickFocus , " ClickFocus " } ,
{ GUI : : FocusPolicy : : NoFocus , " NoFocus " } ,
{ GUI : : FocusPolicy : : TabFocus , " TabFocus " } ,
{ GUI : : FocusPolicy : : StrongFocus , " StrongFocus " } ) ;
2021-01-02 17:27:15 -05:00
register_property (
2023-11-05 19:29:02 -05:00
" foreground_color " sv ,
[ this ] ( ) { return palette ( ) . color ( foreground_role ( ) ) . to_byte_string ( ) ; } ,
: : GUI : : PropertyDeserializer < Color > { } ,
[ this ] ( Gfx : : Color const & color ) {
auto _palette = palette ( ) ;
_palette . set_color ( foreground_role ( ) , color ) ;
set_palette ( _palette ) ;
2021-01-02 17:27:15 -05:00
} ) ;
2021-06-28 13:30:28 +02:00
register_property (
2023-11-05 19:29:02 -05:00
" background_color " sv ,
[ this ] ( ) { return palette ( ) . color ( background_role ( ) ) . to_byte_string ( ) ; } ,
: : GUI : : PropertyDeserializer < Color > { } ,
[ this ] ( Gfx : : Color const & color ) {
set_background_color ( color ) ;
2021-06-28 13:30:28 +02:00
} ) ;
2023-11-05 19:29:02 -05:00
# define __ENUMERATE_COLOR_ROLE(role) \
2024-04-24 06:53:44 -04:00
{ \
Gfx : : ColorRole : : role , # role \
} ,
2023-11-05 19:29:02 -05:00
REGISTER_ENUM_PROPERTY ( " foreground_role " , foreground_role , set_foreground_role , Gfx : : ColorRole ,
{ Gfx : : ColorRole : : NoRole , " NoRole " } ,
ENUMERATE_COLOR_ROLES ( __ENUMERATE_COLOR_ROLE ) ) ;
REGISTER_ENUM_PROPERTY ( " background_role " , background_role , set_background_role , Gfx : : ColorRole ,
{ Gfx : : ColorRole : : NoRole , " NoRole " } ,
ENUMERATE_COLOR_ROLES ( __ENUMERATE_COLOR_ROLE ) ) ;
2021-06-28 13:30:28 +02:00
# undef __ENUMERATE_COLOR_ROLE
2019-01-20 04:49:48 +01:00
}
2022-02-26 10:50:04 -07:00
Widget : : ~ Widget ( ) = default ;
2019-01-20 04:49:48 +01:00
2022-12-06 00:00:35 +11:00
void Widget : : layout_relevant_change_occurred ( )
2022-06-28 17:56:47 +02:00
{
2022-07-05 11:33:25 -04:00
if ( auto * parent = parent_widget ( ) )
2022-12-06 00:00:35 +11:00
parent - > layout_relevant_change_occurred ( ) ;
2022-07-05 11:33:25 -04:00
else if ( window ( ) )
window ( ) - > schedule_relayout ( ) ;
2022-06-28 17:56:47 +02:00
}
2020-02-02 15:07:41 +01:00
void Widget : : child_event ( Core : : ChildEvent & event )
2019-03-15 23:24:40 +01:00
{
2020-02-02 15:07:41 +01:00
if ( event . type ( ) = = Event : : ChildAdded ) {
2020-07-26 17:16:35 +02:00
if ( event . child ( ) & & is < Widget > ( * event . child ( ) ) & & layout ( ) ) {
2021-01-01 16:02:16 +01:00
if ( event . insertion_before_child ( ) & & is < Widget > ( event . insertion_before_child ( ) ) )
2021-06-24 19:53:42 +02:00
layout ( ) - > insert_widget_before ( verify_cast < Widget > ( * event . child ( ) ) , verify_cast < Widget > ( * event . insertion_before_child ( ) ) ) ;
2019-11-05 20:41:27 +01:00
else
2021-06-24 19:53:42 +02:00
layout ( ) - > add_widget ( verify_cast < Widget > ( * event . child ( ) ) ) ;
2022-12-06 00:00:35 +11:00
layout_relevant_change_occurred ( ) ;
2019-11-05 20:41:27 +01:00
}
2020-07-26 17:16:35 +02:00
if ( window ( ) & & event . child ( ) & & is < Widget > ( * event . child ( ) ) )
2021-06-24 19:53:42 +02:00
window ( ) - > did_add_widget ( { } , verify_cast < Widget > ( * event . child ( ) ) ) ;
2022-09-19 20:47:10 +02:00
if ( event . child ( ) & & is < Widget > ( * event . child ( ) ) & & static_cast < Widget const & > ( * event . child ( ) ) . is_visible ( ) ) {
ShowEvent show_event ;
event . child ( ) - > dispatch_event ( show_event ) ;
}
2019-03-15 23:24:40 +01:00
}
2020-02-02 15:07:41 +01:00
if ( event . type ( ) = = Event : : ChildRemoved ) {
2019-04-06 21:15:13 +02:00
if ( layout ( ) ) {
2020-07-26 17:16:35 +02:00
if ( event . child ( ) & & is < Widget > ( * event . child ( ) ) )
2021-06-24 19:53:42 +02:00
layout ( ) - > remove_widget ( verify_cast < Widget > ( * event . child ( ) ) ) ;
2022-12-06 00:00:35 +11:00
layout_relevant_change_occurred ( ) ;
2019-04-06 21:15:13 +02:00
}
2020-07-26 17:16:35 +02:00
if ( window ( ) & & event . child ( ) & & is < Widget > ( * event . child ( ) ) )
2021-06-24 19:53:42 +02:00
window ( ) - > did_remove_widget ( { } , verify_cast < Widget > ( * event . child ( ) ) ) ;
2022-09-19 20:47:10 +02:00
if ( event . child ( ) & & is < Widget > ( * event . child ( ) ) ) {
HideEvent hide_event ;
event . child ( ) - > dispatch_event ( hide_event ) ;
}
2019-04-18 23:25:30 +02:00
update ( ) ;
2019-04-04 01:44:35 +02:00
}
2023-08-06 18:09:39 +02:00
return Core : : EventReceiver : : child_event ( event ) ;
2019-03-15 23:24:40 +01:00
}
2022-04-01 20:58:27 +03:00
void Widget : : set_relative_rect ( Gfx : : IntRect const & a_rect )
2019-01-20 04:49:48 +01:00
{
2019-11-10 12:53:05 +01:00
// Get rid of negative width/height values.
2020-06-10 10:57:59 +02:00
Gfx : : IntRect rect = {
2019-11-10 12:53:05 +01:00
a_rect . x ( ) ,
a_rect . y ( ) ,
max ( a_rect . width ( ) , 0 ) ,
max ( a_rect . height ( ) , 0 )
} ;
2019-10-23 19:51:09 +02:00
2019-01-24 23:40:12 +01:00
if ( rect = = m_relative_rect )
return ;
2019-04-16 22:59:27 +02:00
auto old_rect = m_relative_rect ;
2019-02-20 21:59:13 +01:00
bool size_changed = m_relative_rect . size ( ) ! = rect . size ( ) ;
2019-01-21 00:46:08 +01:00
m_relative_rect = rect ;
2019-02-20 21:59:13 +01:00
if ( size_changed ) {
2020-08-22 13:10:35 +02:00
ResizeEvent resize_event ( rect . size ( ) ) ;
2019-02-20 21:59:13 +01:00
event ( resize_event ) ;
}
2019-04-16 22:59:27 +02:00
if ( auto * parent = parent_widget ( ) )
parent - > update ( old_rect ) ;
2019-01-24 23:40:12 +01:00
update ( ) ;
2019-01-20 04:49:48 +01:00
}
2020-02-02 15:07:41 +01:00
void Widget : : event ( Core : : Event & event )
2019-01-20 04:49:48 +01:00
{
2020-03-04 23:47:27 +01:00
if ( ! is_enabled ( ) ) {
switch ( event . type ( ) ) {
case Event : : MouseUp :
case Event : : MouseDown :
case Event : : MouseMove :
case Event : : MouseWheel :
case Event : : MouseDoubleClick :
case Event : : KeyUp :
case Event : : KeyDown :
return ;
default :
break ;
}
}
2019-01-20 04:49:48 +01:00
switch ( event . type ( ) ) {
2020-02-02 15:07:41 +01:00
case Event : : Paint :
return handle_paint_event ( static_cast < PaintEvent & > ( event ) ) ;
case Event : : Resize :
return handle_resize_event ( static_cast < ResizeEvent & > ( event ) ) ;
case Event : : FocusIn :
2020-08-14 19:56:40 +02:00
return focusin_event ( static_cast < FocusEvent & > ( event ) ) ;
2020-02-02 15:07:41 +01:00
case Event : : FocusOut :
2020-08-14 19:56:40 +02:00
return focusout_event ( static_cast < FocusEvent & > ( event ) ) ;
2020-02-02 15:07:41 +01:00
case Event : : Show :
return show_event ( static_cast < ShowEvent & > ( event ) ) ;
case Event : : Hide :
return hide_event ( static_cast < HideEvent & > ( event ) ) ;
case Event : : KeyDown :
2021-02-13 15:23:13 +01:00
return handle_keydown_event ( static_cast < KeyEvent & > ( event ) ) ;
2020-02-02 15:07:41 +01:00
case Event : : KeyUp :
return keyup_event ( static_cast < KeyEvent & > ( event ) ) ;
case Event : : MouseMove :
return mousemove_event ( static_cast < MouseEvent & > ( event ) ) ;
case Event : : MouseDown :
return handle_mousedown_event ( static_cast < MouseEvent & > ( event ) ) ;
case Event : : MouseDoubleClick :
return handle_mousedoubleclick_event ( static_cast < MouseEvent & > ( event ) ) ;
case Event : : MouseUp :
return handle_mouseup_event ( static_cast < MouseEvent & > ( event ) ) ;
case Event : : MouseWheel :
return mousewheel_event ( static_cast < MouseEvent & > ( event ) ) ;
2021-01-08 22:23:06 +01:00
case Event : : DragEnter :
return drag_enter_event ( static_cast < DragEvent & > ( event ) ) ;
2020-02-13 21:43:32 +01:00
case Event : : DragMove :
return drag_move_event ( static_cast < DragEvent & > ( event ) ) ;
2021-01-08 22:23:06 +01:00
case Event : : DragLeave :
return drag_leave_event ( static_cast < Event & > ( event ) ) ;
2020-02-02 15:07:41 +01:00
case Event : : Drop :
return drop_event ( static_cast < DropEvent & > ( event ) ) ;
2020-03-16 13:36:21 +02:00
case Event : : ThemeChange :
return theme_change_event ( static_cast < ThemeChangeEvent & > ( event ) ) ;
2021-07-12 09:57:34 +02:00
case Event : : FontsChange :
return fonts_change_event ( static_cast < FontsChangeEvent & > ( event ) ) ;
2020-02-02 15:07:41 +01:00
case Event : : Enter :
2019-04-08 18:58:44 +02:00
return handle_enter_event ( event ) ;
2020-02-02 15:07:41 +01:00
case Event : : Leave :
2019-04-08 18:58:44 +02:00
return handle_leave_event ( event ) ;
2020-02-02 15:07:41 +01:00
case Event : : EnabledChange :
return change_event ( static_cast < Event & > ( event ) ) ;
2021-08-20 01:17:38 +02:00
case Event : : ContextMenu :
return context_menu_event ( static_cast < ContextMenuEvent & > ( event ) ) ;
2021-08-24 13:01:01 +01:00
case Event : : AppletAreaRectChange :
return applet_area_rect_change_event ( static_cast < AppletAreaRectChangeEvent & > ( event ) ) ;
2019-01-20 04:49:48 +01:00
default :
2023-08-06 18:09:39 +02:00
return Core : : EventReceiver : : event ( event ) ;
2019-01-20 04:49:48 +01:00
}
}
2021-02-13 15:23:13 +01:00
void Widget : : handle_keydown_event ( KeyEvent & event )
{
keydown_event ( event ) ;
2022-10-24 19:05:40 -05:00
if ( event . is_accepted ( ) )
return ;
if ( auto action = Action : : find_action_for_shortcut ( * this , Shortcut ( event . modifiers ( ) , event . key ( ) ) ) ) {
action - > process_event ( * window ( ) , event ) ;
if ( event . is_accepted ( ) )
return ;
}
2021-02-13 15:23:13 +01:00
if ( event . key ( ) = = KeyCode : : Key_Menu ) {
2023-05-22 00:41:18 +02:00
ContextMenuEvent c_event ( window_relative_rect ( ) . bottom_right ( ) . translated ( - 1 ) , screen_relative_rect ( ) . bottom_right ( ) . translated ( - 1 ) ) ;
2021-08-20 01:17:38 +02:00
dispatch_event ( c_event ) ;
2022-10-24 19:05:40 -05:00
return ;
2021-02-13 15:23:13 +01:00
}
2022-10-24 19:05:40 -05:00
event . ignore ( ) ;
2021-02-13 15:23:13 +01:00
}
2020-02-02 15:07:41 +01:00
void Widget : : handle_paint_event ( PaintEvent & event )
2019-01-20 04:49:48 +01:00
{
2021-02-23 20:42:32 +01:00
VERIFY ( is_visible ( ) ) ;
2021-07-07 01:10:40 +02:00
if ( ! rect ( ) . intersects ( event . rect ( ) ) ) {
// This widget is not inside the paint event rect.
// Since widgets fully contain their children, we don't need to recurse further.
return ;
}
2019-01-21 00:46:08 +01:00
if ( fill_with_background_color ( ) ) {
2020-02-02 15:07:41 +01:00
Painter painter ( * this ) ;
2019-12-24 20:57:54 +01:00
painter . fill_rect ( event . rect ( ) , palette ( ) . color ( background_role ( ) ) ) ;
2019-01-20 04:49:48 +01:00
}
2019-02-09 11:19:38 +01:00
paint_event ( event ) ;
2020-08-25 21:24:45 +02:00
auto children_clip_rect = this - > children_clip_rect ( ) ;
2019-06-07 11:46:02 +02:00
for_each_child_widget ( [ & ] ( auto & child ) {
2019-05-27 03:52:33 +02:00
if ( ! child . is_visible ( ) )
return IterationDecision : : Continue ;
if ( child . relative_rect ( ) . intersects ( event . rect ( ) ) ) {
2020-08-25 21:24:45 +02:00
PaintEvent local_event ( event . rect ( ) . intersected ( children_clip_rect ) . intersected ( child . relative_rect ( ) ) . translated ( - child . relative_position ( ) ) ) ;
2019-09-20 20:37:31 +02:00
child . dispatch_event ( local_event , this ) ;
2019-02-02 08:05:14 +01:00
}
2019-05-27 03:52:33 +02:00
return IterationDecision : : Continue ;
} ) ;
2019-04-11 03:34:37 +02:00
second_paint_event ( event ) ;
2020-03-05 14:42:05 +01:00
2021-01-09 00:11:17 +01:00
auto * app = Application : : the ( ) ;
if ( app & & app - > dnd_debugging_enabled ( ) & & has_pending_drop ( ) ) {
2020-03-05 14:42:05 +01:00
Painter painter ( * this ) ;
2021-01-09 00:11:17 +01:00
painter . draw_rect ( rect ( ) , Color : : Blue ) ;
2020-03-05 14:42:05 +01:00
}
2020-05-12 15:47:13 +02:00
2021-01-09 00:11:17 +01:00
if ( app & & app - > focus_debugging_enabled ( ) & & is_focused ( ) ) {
Painter painter ( * this ) ;
painter . draw_rect ( rect ( ) , Color : : Cyan ) ;
}
2021-07-28 00:58:01 +02:00
if ( app & & app - > hover_debugging_enabled ( ) & & this = = window ( ) - > hovered_widget ( ) ) {
Painter painter ( * this ) ;
painter . draw_rect ( rect ( ) , Color : : Red ) ;
}
2019-01-20 04:49:48 +01:00
}
2020-03-05 09:21:46 +01:00
void Widget : : set_layout ( NonnullRefPtr < Layout > layout )
2019-02-10 11:07:13 +01:00
{
2020-03-05 09:21:46 +01:00
if ( m_layout ) {
2019-05-31 15:44:04 +02:00
m_layout - > notify_disowned ( { } , * this ) ;
2020-03-05 09:21:46 +01:00
m_layout - > remove_from_parent ( ) ;
}
2019-02-10 11:07:13 +01:00
m_layout = move ( layout ) ;
if ( m_layout ) {
2020-03-05 09:21:46 +01:00
add_child ( * m_layout ) ;
2019-05-31 15:44:04 +02:00
m_layout - > notify_adopted ( { } , * this ) ;
2019-02-10 11:07:13 +01:00
do_layout ( ) ;
} else {
update ( ) ;
}
2022-12-06 00:00:35 +11:00
layout_relevant_change_occurred ( ) ;
2019-02-10 11:07:13 +01:00
}
2020-02-02 15:07:41 +01:00
void Widget : : do_layout ( )
2019-02-10 11:07:13 +01:00
{
2019-10-26 12:27:01 +02:00
for_each_child_widget ( [ & ] ( auto & child ) {
child . do_layout ( ) ;
return IterationDecision : : Continue ;
} ) ;
2019-09-01 20:51:20 +02:00
custom_layout ( ) ;
2019-02-10 11:07:13 +01:00
if ( ! m_layout )
return ;
m_layout - > run ( * this ) ;
2020-02-11 11:26:25 +01:00
did_layout ( ) ;
2019-02-10 11:07:13 +01:00
update ( ) ;
}
2020-02-02 15:07:41 +01:00
void Widget : : notify_layout_changed ( Badge < Layout > )
2019-02-10 11:07:13 +01:00
{
2019-04-18 22:57:24 +02:00
invalidate_layout ( ) ;
2019-02-10 11:07:13 +01:00
}
2020-02-02 15:07:41 +01:00
void Widget : : handle_resize_event ( ResizeEvent & event )
2019-02-10 11:07:13 +01:00
{
2019-12-30 00:26:19 +01:00
resize_event ( event ) ;
2019-09-01 20:51:20 +02:00
do_layout ( ) ;
2019-02-10 11:07:13 +01:00
}
2020-02-02 15:07:41 +01:00
void Widget : : handle_mouseup_event ( MouseEvent & event )
2019-03-25 01:42:15 +01:00
{
mouseup_event ( event ) ;
}
2020-02-02 15:07:41 +01:00
void Widget : : handle_mousedown_event ( MouseEvent & event )
2019-04-01 22:03:32 +02:00
{
2021-03-07 03:24:20 -08:00
if ( has_flag ( focus_policy ( ) , FocusPolicy : : ClickFocus ) )
2020-08-14 19:56:40 +02:00
set_focus ( true , FocusSource : : Mouse ) ;
2019-04-01 22:03:32 +02:00
mousedown_event ( event ) ;
2021-10-27 13:20:27 +02:00
if ( event . button ( ) = = MouseButton : : Secondary ) {
2020-02-02 15:07:41 +01:00
ContextMenuEvent c_event ( event . position ( ) , screen_relative_rect ( ) . location ( ) . translated ( event . position ( ) ) ) ;
2021-08-20 01:17:38 +02:00
dispatch_event ( c_event ) ;
2019-04-18 04:12:27 +02:00
}
2019-04-01 22:03:32 +02:00
}
2020-02-02 15:07:41 +01:00
void Widget : : handle_mousedoubleclick_event ( MouseEvent & event )
2019-05-15 22:17:09 +02:00
{
doubleclick_event ( event ) ;
}
2020-02-02 15:07:41 +01:00
void Widget : : handle_enter_event ( Core : : Event & event )
2019-04-08 18:58:44 +02:00
{
2020-10-03 19:43:46 +02:00
if ( auto * window = this - > window ( ) )
window - > update_cursor ( { } ) ;
2021-01-03 17:21:12 +01:00
show_or_hide_tooltip ( ) ;
2019-04-08 18:58:44 +02:00
enter_event ( event ) ;
}
2020-02-02 15:07:41 +01:00
void Widget : : handle_leave_event ( Core : : Event & event )
2019-04-08 18:58:44 +02:00
{
2020-10-03 19:43:46 +02:00
if ( auto * window = this - > window ( ) )
window - > update_cursor ( { } ) ;
2022-08-14 23:23:59 +02:00
if ( Application : : the ( ) - > tooltip_source_widget ( ) = = this )
Application : : the ( ) - > hide_tooltip ( ) ;
2019-04-08 18:58:44 +02:00
leave_event ( event ) ;
}
2020-02-02 15:07:41 +01:00
void Widget : : doubleclick_event ( MouseEvent & )
2019-03-25 01:42:15 +01:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : resize_event ( ResizeEvent & )
2019-02-09 11:19:38 +01:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : paint_event ( PaintEvent & )
2019-02-09 11:19:38 +01:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : second_paint_event ( PaintEvent & )
2019-04-11 03:34:37 +02:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : show_event ( ShowEvent & )
2019-01-20 04:49:48 +01:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : hide_event ( HideEvent & )
2019-01-20 04:49:48 +01:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : keydown_event ( KeyEvent & event )
2019-01-20 04:49:48 +01:00
{
2021-03-11 18:50:23 +01:00
if ( ! event . alt ( ) & & ! event . ctrl ( ) & & ! event . super ( ) ) {
2021-01-02 01:54:30 +01:00
if ( event . key ( ) = = KeyCode : : Key_Tab ) {
if ( event . shift ( ) )
focus_previous_widget ( FocusSource : : Keyboard , false ) ;
else
focus_next_widget ( FocusSource : : Keyboard , false ) ;
event . accept ( ) ;
return ;
}
if ( ! event . shift ( ) & & ( event . key ( ) = = KeyCode : : Key_Left | | event . key ( ) = = KeyCode : : Key_Up ) ) {
focus_previous_widget ( FocusSource : : Keyboard , true ) ;
event . accept ( ) ;
return ;
}
if ( ! event . shift ( ) & & ( event . key ( ) = = KeyCode : : Key_Right | | event . key ( ) = = KeyCode : : Key_Down ) ) {
focus_next_widget ( FocusSource : : Keyboard , true ) ;
event . accept ( ) ;
return ;
}
2019-05-15 02:39:58 +02:00
}
2019-09-20 20:37:31 +02:00
event . ignore ( ) ;
2019-01-20 04:49:48 +01:00
}
2020-07-06 13:08:00 -06:00
void Widget : : keyup_event ( KeyEvent & event )
2019-01-20 04:49:48 +01:00
{
2020-07-06 13:08:00 -06:00
event . ignore ( ) ;
2019-01-20 04:49:48 +01:00
}
2020-02-02 15:07:41 +01:00
void Widget : : mousedown_event ( MouseEvent & )
2019-01-20 04:49:48 +01:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : mouseup_event ( MouseEvent & )
2019-01-20 04:49:48 +01:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : mousemove_event ( MouseEvent & )
2019-01-20 04:49:48 +01:00
{
}
2021-05-04 16:47:35 +02:00
void Widget : : mousewheel_event ( MouseEvent & event )
2019-05-13 19:52:57 +02:00
{
2021-05-04 16:47:35 +02:00
event . ignore ( ) ;
2019-05-13 19:52:57 +02:00
}
2021-08-20 01:17:38 +02:00
void Widget : : context_menu_event ( ContextMenuEvent & event )
2019-04-18 04:12:27 +02:00
{
2021-08-20 01:17:38 +02:00
event . ignore ( ) ;
2019-04-18 04:12:27 +02:00
}
2020-08-14 19:56:40 +02:00
void Widget : : focusin_event ( FocusEvent & )
2019-01-26 11:24:16 +01:00
{
}
2020-08-14 19:56:40 +02:00
void Widget : : focusout_event ( FocusEvent & )
2019-01-26 11:24:16 +01:00
{
}
2021-07-28 21:23:18 +02:00
void Widget : : enter_event ( Core : : Event & )
2019-02-20 10:12:19 +01:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : leave_event ( Core : : Event & )
2019-02-20 10:12:19 +01:00
{
}
2020-02-02 15:07:41 +01:00
void Widget : : change_event ( Event & )
2019-05-25 13:40:57 +02:00
{
}
2021-01-09 00:11:17 +01:00
void Widget : : drag_move_event ( DragEvent & )
2020-02-13 21:43:32 +01:00
{
2021-01-08 22:23:06 +01:00
}
void Widget : : drag_enter_event ( DragEvent & event )
{
2021-01-09 11:01:41 +01:00
StringBuilder builder ;
builder . join ( ' , ' , event . mime_types ( ) ) ;
2022-01-09 23:35:42 +01:00
dbgln_if ( DRAG_DEBUG , " {} {:p} DRAG ENTER @ {}, {} " , class_name ( ) , this , event . position ( ) , builder . string_view ( ) ) ;
2021-01-08 22:23:06 +01:00
}
2021-01-09 00:11:17 +01:00
void Widget : : drag_leave_event ( Event & )
2021-01-08 22:23:06 +01:00
{
2022-01-09 23:35:42 +01:00
dbgln_if ( DRAG_DEBUG , " {} {:p} DRAG LEAVE " , class_name ( ) , this ) ;
2020-02-13 21:43:32 +01:00
}
2020-02-02 15:07:41 +01:00
void Widget : : drop_event ( DropEvent & event )
2019-12-08 16:50:23 +01:00
{
2022-01-09 23:35:42 +01:00
dbgln_if ( DRAG_DEBUG , " {} {:p} DROP @ {}, '{}' " , class_name ( ) , this , event . position ( ) , event . text ( ) ) ;
2021-07-12 13:25:52 +02:00
event . ignore ( ) ;
2019-12-08 16:50:23 +01:00
}
2020-03-16 13:36:21 +02:00
void Widget : : theme_change_event ( ThemeChangeEvent & )
{
}
2021-07-12 09:57:34 +02:00
void Widget : : fonts_change_event ( FontsChangeEvent & )
{
2021-07-12 10:05:05 +02:00
if ( m_default_font )
set_font ( nullptr ) ;
2021-07-12 09:57:34 +02:00
}
2021-06-13 06:16:06 -06:00
void Widget : : screen_rects_change_event ( ScreenRectsChangeEvent & )
2021-04-04 00:02:22 +02:00
{
}
2021-08-24 13:01:01 +01:00
void Widget : : applet_area_rect_change_event ( AppletAreaRectChangeEvent & )
{
}
2020-02-02 15:07:41 +01:00
void Widget : : update ( )
2019-02-10 14:28:39 +01:00
{
2019-05-02 03:46:37 +02:00
if ( rect ( ) . is_empty ( ) )
return ;
2019-02-10 14:28:39 +01:00
update ( rect ( ) ) ;
2022-11-17 11:15:34 -05:00
for ( auto & it : m_focus_delegators ) {
if ( ! it . is_null ( ) & & ! it - > rect ( ) . is_empty ( ) )
it - > update ( it - > rect ( ) ) ;
}
2019-02-10 14:28:39 +01:00
}
2022-04-01 20:58:27 +03:00
void Widget : : update ( Gfx : : IntRect const & rect )
2019-01-20 04:49:48 +01:00
{
2019-03-25 13:58:30 +01:00
if ( ! is_visible ( ) )
return ;
2019-05-02 03:46:37 +02:00
2019-05-02 04:19:59 +02:00
if ( ! updates_enabled ( ) )
return ;
2021-02-15 19:57:30 -07:00
auto bound_by_widget = rect . intersected ( this - > rect ( ) ) ;
if ( bound_by_widget . is_empty ( ) )
return ;
2020-02-02 15:07:41 +01:00
Window * window = m_window ;
Widget * parent = parent_widget ( ) ;
2019-05-02 03:46:37 +02:00
while ( parent ) {
if ( ! parent - > updates_enabled ( ) )
return ;
window = parent - > m_window ;
parent = parent - > parent_widget ( ) ;
}
if ( window )
2021-02-15 19:57:30 -07:00
window - > update ( bound_by_widget . translated ( window_relative_rect ( ) . location ( ) ) ) ;
2019-02-09 14:30:05 +01:00
}
2021-10-23 17:53:11 +02:00
void Widget : : repaint ( )
{
if ( rect ( ) . is_empty ( ) )
return ;
repaint ( rect ( ) ) ;
}
void Widget : : repaint ( Gfx : : IntRect const & rect )
{
auto * window = this - > window ( ) ;
if ( ! window )
return ;
update ( rect ) ;
window - > flush_pending_paints_immediately ( ) ;
}
2020-06-10 10:57:59 +02:00
Gfx : : IntRect Widget : : window_relative_rect ( ) const
2019-02-09 14:30:05 +01:00
{
auto rect = relative_rect ( ) ;
for ( auto * parent = parent_widget ( ) ; parent ; parent = parent - > parent_widget ( ) ) {
2021-04-12 11:47:09 -07:00
rect . translate_by ( parent - > relative_position ( ) ) ;
2019-02-09 14:30:05 +01:00
}
return rect ;
2019-01-20 04:49:48 +01:00
}
2020-06-10 10:57:59 +02:00
Gfx : : IntRect Widget : : screen_relative_rect ( ) const
2019-04-08 18:58:44 +02:00
{
2021-04-04 17:55:50 +02:00
auto window_position = window ( ) - > window_type ( ) = = WindowType : : Applet
2021-03-30 23:30:50 +02:00
? window ( ) - > applet_rect_on_screen ( ) . location ( )
2020-08-13 21:15:13 +02:00
: window ( ) - > rect ( ) . location ( ) ;
return window_relative_rect ( ) . translated ( window_position ) ;
2019-04-08 18:58:44 +02:00
}
2022-12-06 20:27:44 +00:00
Widget * Widget : : child_at ( Gfx : : IntPoint point ) const
2019-01-20 04:49:48 +01:00
{
2019-04-10 02:08:32 +02:00
for ( int i = children ( ) . size ( ) - 1 ; i > = 0 ; - - i ) {
2020-07-26 17:16:35 +02:00
if ( ! is < Widget > ( children ( ) [ i ] ) )
2019-03-15 16:45:27 +01:00
continue ;
2023-03-06 14:17:01 +01:00
auto & child = verify_cast < Widget > ( * children ( ) [ i ] ) ;
2019-03-15 16:45:27 +01:00
if ( ! child . is_visible ( ) )
continue ;
2021-09-14 21:08:57 +02:00
if ( child . relative_non_grabbable_rect ( ) . contains ( point ) )
2020-02-02 15:07:41 +01:00
return const_cast < Widget * > ( & child ) ;
2019-01-20 04:49:48 +01:00
}
2019-04-16 03:47:55 +02:00
return nullptr ;
}
2022-12-06 20:27:44 +00:00
Widget : : HitTestResult Widget : : hit_test ( Gfx : : IntPoint position , ShouldRespectGreediness should_respect_greediness )
2019-04-16 03:47:55 +02:00
{
2019-09-17 20:58:13 +02:00
if ( should_respect_greediness = = ShouldRespectGreediness : : Yes & & is_greedy_for_hits ( ) )
2019-04-16 13:25:00 +02:00
return { this , position } ;
if ( auto * child = child_at ( position ) )
return child - > hit_test ( position - child - > relative_position ( ) ) ;
return { this , position } ;
2019-01-20 04:49:48 +01:00
}
2020-02-02 15:07:41 +01:00
void Widget : : set_window ( Window * window )
2019-01-20 04:49:48 +01:00
{
if ( m_window = = window )
return ;
m_window = window ;
}
2020-08-25 11:21:49 +02:00
void Widget : : set_focus_proxy ( Widget * proxy )
{
if ( m_focus_proxy = = proxy )
return ;
2022-11-17 11:15:34 -05:00
if ( proxy )
proxy - > add_focus_delegator ( this ) ;
else if ( m_focus_proxy )
m_focus_proxy - > remove_focus_delegator ( this ) ;
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 16:26:13 -06:00
m_focus_proxy = proxy ;
2020-08-25 11:21:49 +02:00
}
2022-11-17 11:15:34 -05:00
void Widget : : add_focus_delegator ( Widget * delegator )
{
m_focus_delegators . remove_all_matching ( [ & ] ( auto & entry ) {
return entry . is_null ( ) | | entry = = delegator ;
} ) ;
m_focus_delegators . append ( delegator ) ;
}
void Widget : : remove_focus_delegator ( Widget * delegator )
{
m_focus_delegators . remove_first_matching ( [ & ] ( auto & entry ) {
return entry = = delegator ;
} ) ;
}
2020-10-30 10:58:27 +01:00
FocusPolicy Widget : : focus_policy ( ) const
{
if ( m_focus_proxy )
return m_focus_proxy - > focus_policy ( ) ;
return m_focus_policy ;
}
void Widget : : set_focus_policy ( FocusPolicy policy )
{
if ( m_focus_proxy )
return m_focus_proxy - > set_focus_policy ( policy ) ;
m_focus_policy = policy ;
}
2020-02-02 15:07:41 +01:00
bool Widget : : is_focused ( ) const
2019-01-20 04:49:48 +01:00
{
2020-08-25 11:21:49 +02:00
if ( m_focus_proxy )
return m_focus_proxy - > is_focused ( ) ;
2019-01-26 11:24:16 +01:00
auto * win = window ( ) ;
if ( ! win )
return false ;
2022-11-17 10:00:07 -05:00
if ( win - > is_focusable ( ) )
2020-07-14 19:17:00 -06:00
return win - > focused_widget ( ) = = this ;
return false ;
2019-01-20 04:49:48 +01:00
}
2020-08-14 19:56:40 +02:00
void Widget : : set_focus ( bool focus , FocusSource source )
2019-01-20 04:49:48 +01:00
{
2020-08-25 11:21:49 +02:00
if ( m_focus_proxy )
return m_focus_proxy - > set_focus ( focus , source ) ;
2019-01-26 11:24:16 +01:00
auto * win = window ( ) ;
if ( ! win )
2019-01-20 04:49:48 +01:00
return ;
2019-01-26 11:24:16 +01:00
if ( focus ) {
2020-08-14 19:56:40 +02:00
win - > set_focused_widget ( this , source ) ;
2019-01-26 11:24:16 +01:00
} else {
if ( win - > focused_widget ( ) = = this )
2020-08-14 19:56:40 +02:00
win - > set_focused_widget ( nullptr , source ) ;
2019-01-26 11:24:16 +01:00
}
2019-01-20 04:49:48 +01:00
}
2022-04-01 20:58:27 +03:00
void Widget : : set_font ( Gfx : : Font const * font )
2019-01-20 04:49:48 +01:00
{
2019-09-01 12:26:35 +02:00
if ( m_font . ptr ( ) = = font )
return ;
2021-07-12 10:05:05 +02:00
if ( ! font ) {
2020-12-29 18:25:13 +01:00
m_font = Gfx : : FontDatabase : : default_font ( ) ;
2021-07-12 10:05:05 +02:00
m_default_font = true ;
} else {
2019-09-01 12:26:35 +02:00
m_font = * font ;
2021-07-12 10:05:05 +02:00
m_default_font = false ;
}
2019-09-01 12:26:35 +02:00
did_change_font ( ) ;
2019-02-12 10:08:35 +01:00
update ( ) ;
2019-01-20 04:49:48 +01:00
}
2019-01-27 08:48:34 +01:00
2023-08-01 20:28:44 +01:00
void Widget : : set_font_family ( String const & family )
2021-03-22 19:51:06 +00:00
{
2023-09-05 20:19:33 +02:00
set_font ( Gfx : : FontDatabase : : the ( ) . get ( family , m_font - > presentation_size ( ) , m_font - > weight ( ) , m_font - > width ( ) , m_font - > slope ( ) ) ) ;
2021-03-22 19:51:06 +00:00
}
void Widget : : set_font_size ( unsigned size )
{
2023-09-05 20:19:33 +02:00
set_font ( Gfx : : FontDatabase : : the ( ) . get ( m_font - > family ( ) , size , m_font - > weight ( ) , m_font - > width ( ) , m_font - > slope ( ) ) ) ;
2021-03-22 19:51:06 +00:00
}
void Widget : : set_font_weight ( unsigned weight )
{
2023-09-05 20:19:33 +02:00
set_font ( Gfx : : FontDatabase : : the ( ) . get ( m_font - > family ( ) , m_font - > presentation_size ( ) , weight , m_font - > width ( ) , m_font - > slope ( ) ) ) ;
2021-03-22 19:51:06 +00:00
}
void Widget : : set_font_fixed_width ( bool fixed_width )
{
if ( fixed_width )
2023-09-05 20:19:33 +02:00
set_font ( Gfx : : FontDatabase : : the ( ) . get ( Gfx : : FontDatabase : : the ( ) . default_fixed_width_font ( ) . family ( ) , m_font - > presentation_size ( ) , m_font - > weight ( ) , m_font - > width ( ) , m_font - > slope ( ) ) ) ;
2021-03-22 19:51:06 +00:00
else
2023-09-05 20:19:33 +02:00
set_font ( Gfx : : FontDatabase : : the ( ) . get ( Gfx : : FontDatabase : : the ( ) . default_font ( ) . family ( ) , m_font - > presentation_size ( ) , m_font - > weight ( ) , m_font - > width ( ) , m_font - > slope ( ) ) ) ;
2021-03-22 19:51:06 +00:00
}
2023-05-26 14:07:41 +02:00
bool Widget : : is_font_fixed_width ( )
{
return font ( ) . is_fixed_width ( ) ;
}
2022-06-12 21:19:41 +02:00
void Widget : : set_min_size ( UISize const & size )
2020-12-29 18:22:51 +01:00
{
2022-06-12 21:19:41 +02:00
VERIFY ( size . width ( ) . is_one_of ( SpecialDimension : : Regular , SpecialDimension : : Shrink ) ) ;
2020-12-29 18:22:51 +01:00
if ( m_min_size = = size )
return ;
m_min_size = size ;
2022-12-06 00:00:35 +11:00
layout_relevant_change_occurred ( ) ;
2020-12-29 18:22:51 +01:00
}
2022-06-12 21:19:41 +02:00
void Widget : : set_max_size ( UISize const & size )
2020-12-29 18:22:51 +01:00
{
2022-06-12 21:19:41 +02:00
VERIFY ( size . width ( ) . is_one_of ( SpecialDimension : : Regular , SpecialDimension : : Grow ) ) ;
2020-12-29 18:22:51 +01:00
if ( m_max_size = = size )
return ;
m_max_size = size ;
2022-12-06 00:00:35 +11:00
layout_relevant_change_occurred ( ) ;
2020-12-29 18:22:51 +01:00
}
2022-06-12 22:16:39 +02:00
void Widget : : set_preferred_size ( UISize const & size )
{
if ( m_preferred_size = = size )
return ;
m_preferred_size = size ;
2022-12-06 00:00:35 +11:00
layout_relevant_change_occurred ( ) ;
2022-06-12 22:16:39 +02:00
}
2022-06-12 23:10:50 +02:00
Optional < UISize > Widget : : calculated_preferred_size ( ) const
{
if ( layout ( ) )
return { layout ( ) - > preferred_size ( ) } ;
return { } ;
}
Optional < UISize > Widget : : calculated_min_size ( ) const
{
if ( layout ( ) )
return { layout ( ) - > min_size ( ) } ;
// Fall back to at least displaying the margins, so the Widget is not 0 size.
auto m = content_margins ( ) ;
if ( ! m . is_null ( ) )
return UISize { m . left ( ) + m . right ( ) , m . top ( ) + m . bottom ( ) } ;
return { } ;
}
2020-02-02 15:07:41 +01:00
void Widget : : invalidate_layout ( )
2019-02-10 11:07:13 +01:00
{
2019-10-26 12:27:01 +02:00
if ( window ( ) )
window ( ) - > schedule_relayout ( ) ;
2019-02-10 11:07:13 +01:00
}
2019-03-15 16:12:06 +01:00
2020-02-02 15:07:41 +01:00
void Widget : : set_visible ( bool visible )
2019-03-15 16:12:06 +01:00
{
if ( visible = = m_visible )
return ;
m_visible = visible ;
2022-12-06 00:00:35 +11:00
layout_relevant_change_occurred ( ) ;
2019-03-15 16:12:06 +01:00
if ( m_visible )
update ( ) ;
2021-06-01 22:08:47 -06:00
if ( ! m_visible & & is_focused ( ) )
set_focus ( false ) ;
2019-10-02 20:24:03 +02:00
if ( m_visible ) {
2020-02-02 15:07:41 +01:00
ShowEvent e ;
2019-10-02 20:24:03 +02:00
event ( e ) ;
} else {
2020-02-02 15:07:41 +01:00
HideEvent e ;
2019-10-02 20:24:03 +02:00
event ( e ) ;
}
2019-03-15 16:12:06 +01:00
}
2019-03-29 02:20:22 +01:00
2020-02-02 15:07:41 +01:00
bool Widget : : spans_entire_window_horizontally ( ) const
2019-03-29 02:20:22 +01:00
{
auto * w = window ( ) ;
if ( ! w )
return false ;
auto * main_widget = w - > main_widget ( ) ;
if ( ! main_widget )
return false ;
if ( main_widget = = this )
return true ;
auto wrr = window_relative_rect ( ) ;
return wrr . left ( ) = = main_widget - > rect ( ) . left ( ) & & wrr . right ( ) = = main_widget - > rect ( ) . right ( ) ;
}
2019-04-12 02:51:16 +02:00
2020-02-02 15:07:41 +01:00
void Widget : : set_enabled ( bool enabled )
2019-04-12 02:51:16 +02:00
{
if ( m_enabled = = enabled )
return ;
m_enabled = enabled ;
2020-05-08 13:49:58 +02:00
for_each_child_widget ( [ enabled ] ( auto & child ) {
child . set_enabled ( enabled ) ;
return IterationDecision : : Continue ;
} ) ;
2020-12-28 00:33:46 +01:00
if ( ! m_enabled & & window ( ) & & window ( ) - > focused_widget ( ) = = this ) {
window ( ) - > did_disable_focused_widget ( { } ) ;
}
2021-03-09 16:58:03 -05:00
if ( ! m_enabled )
set_override_cursor ( Gfx : : StandardCursor : : None ) ;
2020-02-02 15:07:41 +01:00
Event e ( Event : : EnabledChange ) ;
2019-05-25 13:40:57 +02:00
event ( e ) ;
2019-04-12 02:51:16 +02:00
update ( ) ;
}
2019-04-12 17:10:30 +02:00
2020-02-02 15:07:41 +01:00
void Widget : : move_to_front ( )
2019-04-16 03:47:55 +02:00
{
auto * parent = parent_widget ( ) ;
if ( ! parent )
return ;
if ( parent - > children ( ) . size ( ) = = 1 )
return ;
2019-06-07 11:46:02 +02:00
parent - > children ( ) . remove_first_matching ( [ this ] ( auto & entry ) {
2019-04-16 04:01:14 +02:00
return entry = = this ;
} ) ;
2019-09-22 00:17:53 +02:00
parent - > children ( ) . append ( * this ) ;
2019-04-16 04:01:14 +02:00
parent - > update ( ) ;
2019-04-16 03:47:55 +02:00
}
2020-02-02 15:07:41 +01:00
void Widget : : move_to_back ( )
2019-04-16 03:47:55 +02:00
{
auto * parent = parent_widget ( ) ;
if ( ! parent )
return ;
if ( parent - > children ( ) . size ( ) = = 1 )
return ;
2019-06-07 11:46:02 +02:00
parent - > children ( ) . remove_first_matching ( [ this ] ( auto & entry ) {
2019-04-16 04:01:14 +02:00
return entry = = this ;
} ) ;
2019-09-22 00:17:53 +02:00
parent - > children ( ) . prepend ( * this ) ;
2019-04-16 04:01:14 +02:00
parent - > update ( ) ;
2019-04-16 03:47:55 +02:00
}
2020-02-02 15:07:41 +01:00
bool Widget : : is_frontmost ( ) const
2019-04-16 03:47:55 +02:00
{
auto * parent = parent_widget ( ) ;
if ( ! parent )
return true ;
2023-03-06 14:17:01 +01:00
return parent - > children ( ) . last ( ) = = this ;
2019-04-16 03:47:55 +02:00
}
2020-02-02 15:07:41 +01:00
bool Widget : : is_backmost ( ) const
2019-04-16 03:47:55 +02:00
{
auto * parent = parent_widget ( ) ;
if ( ! parent )
return true ;
2023-03-06 14:17:01 +01:00
return parent - > children ( ) . first ( ) = = this ;
2019-04-12 17:10:30 +02:00
}
2019-04-20 21:56:56 +02:00
2022-02-05 18:39:34 +11:00
Action * Widget : : action_for_shortcut ( Shortcut const & shortcut )
2019-04-20 21:56:56 +02:00
{
2022-10-24 19:04:32 -05:00
return Action : : find_action_for_shortcut ( * this , shortcut ) ;
2019-04-20 21:56:56 +02:00
}
2019-05-02 03:46:37 +02:00
2020-02-02 15:07:41 +01:00
void Widget : : set_updates_enabled ( bool enabled )
2019-05-02 03:46:37 +02:00
{
if ( m_updates_enabled = = enabled )
return ;
m_updates_enabled = enabled ;
if ( enabled )
update ( ) ;
}
2019-05-15 02:39:58 +02:00
2021-01-02 01:54:30 +01:00
void Widget : : focus_previous_widget ( FocusSource source , bool siblings_only )
2019-05-15 02:39:58 +02:00
{
2020-10-30 10:58:27 +01:00
auto focusable_widgets = window ( ) - > focusable_widgets ( source ) ;
2021-01-02 01:54:30 +01:00
if ( siblings_only )
2021-06-08 19:36:27 +04:30
focusable_widgets . remove_all_matching ( [ this ] ( auto & entry ) { return entry . parent ( ) ! = parent ( ) ; } ) ;
2019-05-15 02:39:58 +02:00
for ( int i = focusable_widgets . size ( ) - 1 ; i > = 0 ; - - i ) {
2021-06-08 19:36:27 +04:30
if ( & focusable_widgets [ i ] ! = this )
2019-05-15 02:39:58 +02:00
continue ;
if ( i > 0 )
2021-06-08 19:36:27 +04:30
focusable_widgets [ i - 1 ] . set_focus ( true , source ) ;
2019-05-15 02:39:58 +02:00
else
2021-06-08 19:36:27 +04:30
focusable_widgets . last ( ) . set_focus ( true , source ) ;
2019-05-15 02:39:58 +02:00
}
}
2021-01-02 01:54:30 +01:00
void Widget : : focus_next_widget ( FocusSource source , bool siblings_only )
2019-05-15 02:39:58 +02:00
{
2020-10-30 10:58:27 +01:00
auto focusable_widgets = window ( ) - > focusable_widgets ( source ) ;
2021-01-02 01:54:30 +01:00
if ( siblings_only )
2021-06-08 19:36:27 +04:30
focusable_widgets . remove_all_matching ( [ this ] ( auto & entry ) { return entry . parent ( ) ! = parent ( ) ; } ) ;
2020-02-25 14:49:47 +01:00
for ( size_t i = 0 ; i < focusable_widgets . size ( ) ; + + i ) {
2021-06-08 19:36:27 +04:30
if ( & focusable_widgets [ i ] ! = this )
2019-05-15 02:39:58 +02:00
continue ;
if ( i < focusable_widgets . size ( ) - 1 )
2021-06-08 19:36:27 +04:30
focusable_widgets [ i + 1 ] . set_focus ( true , source ) ;
2019-05-15 02:39:58 +02:00
else
2021-06-08 19:36:27 +04:30
focusable_widgets . first ( ) . set_focus ( true , source ) ;
2019-05-15 02:39:58 +02:00
}
}
2019-08-03 11:35:10 +02:00
2021-06-08 19:36:27 +04:30
Vector < Widget & > Widget : : child_widgets ( ) const
2019-11-11 19:12:32 +01:00
{
2021-06-08 19:36:27 +04:30
Vector < Widget & > widgets ;
2019-11-11 19:12:32 +01:00
widgets . ensure_capacity ( children ( ) . size ( ) ) ;
2020-02-02 15:07:41 +01:00
for ( auto & child : const_cast < Widget * > ( this ) - > children ( ) ) {
2023-03-06 14:17:01 +01:00
if ( is < Widget > ( * child ) )
widgets . append ( static_cast < Widget & > ( * child ) ) ;
2019-11-11 19:12:32 +01:00
}
return widgets ;
}
2019-12-24 20:57:54 +01:00
2023-02-20 19:03:44 +01:00
void Widget : : set_palette ( Palette & palette )
2019-12-24 20:57:54 +01:00
{
2019-12-29 00:47:49 +01:00
m_palette = palette . impl ( ) ;
2021-06-24 09:10:40 -04:00
update ( ) ;
2019-12-24 20:57:54 +01:00
}
2020-02-02 15:07:41 +01:00
2023-03-10 18:40:34 +01:00
void Widget : : set_title ( String title )
2022-03-18 22:54:30 +01:00
{
m_title = move ( title ) ;
2022-12-06 00:00:35 +11:00
layout_relevant_change_occurred ( ) ;
2022-03-18 22:54:30 +01:00
// For tab widget children, our change in title also affects the parent.
if ( parent_widget ( ) )
parent_widget ( ) - > update ( ) ;
}
2023-03-10 18:40:34 +01:00
String Widget : : title ( ) const
2022-03-18 22:54:30 +01:00
{
return m_title ;
}
2020-02-14 23:53:11 +01:00
void Widget : : set_background_role ( ColorRole role )
{
m_background_role = role ;
2021-06-24 09:10:40 -04:00
update ( ) ;
2020-02-14 23:53:11 +01:00
}
void Widget : : set_foreground_role ( ColorRole role )
{
m_foreground_role = role ;
2021-06-24 09:10:40 -04:00
update ( ) ;
2020-02-14 23:53:11 +01:00
}
2023-11-11 13:38:35 +01:00
void Widget : : set_background_color ( Gfx : : Color color )
{
auto _palette = palette ( ) ;
_palette . set_color ( background_role ( ) , color ) ;
set_palette ( _palette ) ;
}
2020-02-14 23:53:11 +01:00
Gfx : : Palette Widget : : palette ( ) const
{
return Gfx : : Palette ( * m_palette ) ;
}
2022-04-01 20:58:27 +03:00
void Widget : : set_grabbable_margins ( Margins const & margins )
2020-04-24 18:55:29 +02:00
{
2021-09-14 21:08:57 +02:00
if ( m_grabbable_margins = = margins )
2020-04-24 18:55:29 +02:00
return ;
2021-09-14 21:08:57 +02:00
m_grabbable_margins = margins ;
2022-12-06 00:00:35 +11:00
layout_relevant_change_occurred ( ) ;
2020-04-24 18:55:29 +02:00
}
2021-09-14 21:08:57 +02:00
Gfx : : IntRect Widget : : relative_non_grabbable_rect ( ) const
2020-04-24 18:55:29 +02:00
{
auto rect = relative_rect ( ) ;
2021-09-14 21:08:57 +02:00
rect . translate_by ( m_grabbable_margins . left ( ) , m_grabbable_margins . top ( ) ) ;
rect . set_width ( rect . width ( ) - ( m_grabbable_margins . left ( ) + m_grabbable_margins . right ( ) ) ) ;
rect . set_height ( rect . height ( ) - ( m_grabbable_margins . top ( ) + m_grabbable_margins . bottom ( ) ) ) ;
2020-04-24 18:55:29 +02:00
return rect ;
}
2023-08-01 20:28:44 +01:00
void Widget : : set_tooltip ( String tooltip )
2020-08-15 11:44:34 +02:00
{
2021-04-16 19:12:37 +02:00
m_tooltip = move ( tooltip ) ;
2021-01-03 17:21:12 +01:00
if ( Application : : the ( ) - > tooltip_source_widget ( ) = = this )
show_or_hide_tooltip ( ) ;
2020-08-15 11:44:34 +02:00
}
2021-01-03 17:21:12 +01:00
void Widget : : show_or_hide_tooltip ( )
2020-08-15 11:44:34 +02:00
{
if ( has_tooltip ( ) )
2023-09-10 16:44:32 +02:00
Application : : the ( ) - > show_tooltip ( m_tooltip , this ) ;
2021-01-03 17:21:12 +01:00
else
Application : : the ( ) - > hide_tooltip ( ) ;
2020-08-15 11:44:34 +02:00
}
2020-08-25 21:24:45 +02:00
Gfx : : IntRect Widget : : children_clip_rect ( ) const
{
return rect ( ) ;
}
2023-02-20 19:03:44 +01:00
void Widget : : set_override_cursor ( AK : : Variant < Gfx : : StandardCursor , NonnullRefPtr < Gfx : : Bitmap const > > cursor )
2020-09-11 14:17:35 +02:00
{
2023-04-30 10:42:54 +02:00
if ( m_override_cursor = = cursor )
2020-09-11 14:17:35 +02:00
return ;
2021-10-15 22:24:41 +02:00
m_override_cursor = move ( cursor ) ;
if ( auto * window = this - > window ( ) ) {
2020-09-11 14:17:35 +02:00
window - > update_cursor ( { } ) ;
2021-10-15 22:24:41 +02:00
}
2020-09-11 14:17:35 +02:00
}
2023-01-07 12:38:23 +00:00
ErrorOr < void > Widget : : load_from_gml ( StringView gml_string )
2021-01-11 14:01:15 -05:00
{
2023-08-01 20:28:44 +01:00
return load_from_gml ( gml_string , [ ] ( StringView class_name ) - > ErrorOr < NonnullRefPtr < Core : : EventReceiver > > {
2021-01-11 14:01:15 -05:00
dbgln ( " Class '{}' not registered " , class_name ) ;
2022-12-30 11:33:58 +00:00
return Error : : from_string_literal ( " Class not registered " ) ;
2021-01-11 14:01:15 -05:00
} ) ;
}
2023-01-07 12:38:23 +00:00
ErrorOr < void > Widget : : load_from_gml ( StringView gml_string , UnregisteredChildHandler unregistered_child_handler )
2022-12-28 20:35:16 +00:00
{
auto value = TRY ( GML : : parse_gml ( gml_string ) ) ;
return load_from_gml_ast ( value , unregistered_child_handler ) ;
}
2023-02-20 19:03:44 +01:00
ErrorOr < void > Widget : : load_from_gml_ast ( NonnullRefPtr < GUI : : GML : : Node const > ast , UnregisteredChildHandler unregistered_child_handler )
2020-09-14 12:46:53 +02:00
{
2022-02-04 17:19:22 +01:00
if ( is < GUI : : GML : : GMLFile > ( ast . ptr ( ) ) )
2023-02-20 19:03:44 +01:00
return load_from_gml_ast ( static_cast < GUI : : GML : : GMLFile const & > ( * ast ) . main_class ( ) , unregistered_child_handler ) ;
2022-02-04 17:19:22 +01:00
VERIFY ( is < GUI : : GML : : Object > ( ast . ptr ( ) ) ) ;
2023-02-20 19:03:44 +01:00
auto const & object = static_cast < GUI : : GML : : Object const & > ( * ast ) ;
2022-02-04 17:19:22 +01:00
2023-02-20 19:03:44 +01:00
object . for_each_property ( [ & ] ( auto key , auto value ) {
2020-09-14 16:01:12 +02:00
set_property ( key , value ) ;
} ) ;
2020-09-14 12:46:53 +02:00
2023-02-20 19:03:44 +01:00
auto layout = object . layout_object ( ) ;
2022-02-04 17:19:22 +01:00
if ( ! layout . is_null ( ) ) {
auto class_name = layout - > name ( ) ;
2020-09-14 12:46:53 +02:00
if ( class_name . is_null ( ) ) {
2022-12-28 20:35:16 +00:00
return Error : : from_string_literal ( " Invalid layout class name " ) ;
2020-09-14 12:46:53 +02:00
}
2023-08-06 15:14:59 +02:00
auto & layout_class = * GUI : : ObjectClassRegistration : : find ( " GUI::Layout " sv ) ;
if ( auto * registration = GUI : : ObjectClassRegistration : : find ( class_name ) ) {
2022-12-28 20:35:16 +00:00
auto layout = TRY ( registration - > construct ( ) ) ;
if ( ! registration - > is_derived_from ( layout_class ) ) {
2023-12-16 17:49:34 +03:30
dbgln ( " Invalid layout class: '{}' " , class_name . to_byte_string ( ) ) ;
2022-12-28 20:35:16 +00:00
return Error : : from_string_literal ( " Invalid layout class " ) ;
2021-10-26 02:12:16 -05:00
}
2022-12-28 20:35:16 +00:00
set_layout ( static_ptr_cast < Layout > ( layout ) ) ;
2020-09-14 12:46:53 +02:00
} else {
2023-12-16 17:49:34 +03:30
dbgln ( " Unknown layout class: '{}' " , class_name . to_byte_string ( ) ) ;
2022-12-28 20:35:16 +00:00
return Error : : from_string_literal ( " Unknown layout class " ) ;
2020-09-14 12:46:53 +02:00
}
2022-02-04 17:19:22 +01:00
layout - > for_each_property ( [ & ] ( auto key , auto value ) {
2020-09-15 21:33:37 +02:00
this - > layout ( ) - > set_property ( key , value ) ;
} ) ;
2020-09-14 12:46:53 +02:00
}
2023-08-06 15:14:59 +02:00
auto & widget_class = * GUI : : ObjectClassRegistration : : find ( " GUI::Widget " sv ) ;
2022-03-18 22:57:05 +01:00
bool is_tab_widget = is < TabWidget > ( * this ) ;
2023-02-20 19:03:44 +01:00
TRY ( object . try_for_each_child_object ( [ & ] ( auto const & child_data ) - > ErrorOr < void > {
auto class_name = child_data . name ( ) ;
2022-02-04 17:19:22 +01:00
2022-01-04 18:15:15 +01:00
// It is very questionable if this pseudo object should exist, but it works fine like this for now.
if ( class_name = = " GUI::Layout::Spacer " ) {
if ( ! this - > layout ( ) ) {
2022-12-28 20:35:16 +00:00
return Error : : from_string_literal ( " Specified GUI::Layout::Spacer in GML, but the parent has no Layout. " ) ;
2020-09-14 12:46:53 +02:00
}
2023-08-13 18:29:05 +02:00
add_spacer ( ) ;
2022-02-04 17:19:22 +01:00
} else {
2023-08-06 18:09:39 +02:00
RefPtr < Core : : EventReceiver > child ;
2023-08-06 15:14:59 +02:00
if ( auto * registration = GUI : : ObjectClassRegistration : : find ( class_name ) ) {
2022-12-28 20:35:16 +00:00
child = TRY ( registration - > construct ( ) ) ;
if ( ! registration - > is_derived_from ( widget_class ) ) {
2022-01-04 18:15:15 +01:00
dbgln ( " Invalid widget class: '{}' " , class_name ) ;
2022-12-28 20:35:16 +00:00
return Error : : from_string_literal ( " Invalid widget class " ) ;
2022-01-04 18:15:15 +01:00
}
} else {
2022-12-30 11:33:58 +00:00
child = TRY ( unregistered_child_handler ( class_name ) ) ;
2022-01-04 18:15:15 +01:00
}
if ( ! child )
2022-12-28 20:35:16 +00:00
return Error : : from_string_literal ( " Unable to construct a Widget class for child " ) ;
2022-01-04 18:15:15 +01:00
add_child ( * child ) ;
2022-03-18 22:57:05 +01:00
2022-01-04 18:15:15 +01:00
// This is possible as we ensure that Widget is a base class above.
2022-12-28 20:35:16 +00:00
TRY ( static_ptr_cast < Widget > ( child ) - > load_from_gml_ast ( child_data , unregistered_child_handler ) ) ;
2022-03-18 22:57:05 +01:00
2022-01-04 18:15:15 +01:00
if ( is_tab_widget ) {
// FIXME: We need to have the child added before loading it so that it can access us. But the TabWidget logic requires the child to not be present yet.
remove_child ( * child ) ;
reinterpret_cast < TabWidget * > ( this ) - > add_widget ( * static_ptr_cast < Widget > ( child ) ) ;
}
2022-03-18 22:57:05 +01:00
}
2022-12-28 20:35:16 +00:00
return { } ;
} ) ) ;
2020-09-14 12:46:53 +02:00
2022-12-28 20:35:16 +00:00
return { } ;
2020-09-14 12:46:53 +02:00
}
2020-10-30 23:38:42 +01:00
bool Widget : : has_focus_within ( ) const
{
auto * window = this - > window ( ) ;
if ( ! window )
return false ;
if ( ! window - > focused_widget ( ) )
return false ;
auto & effective_focus_widget = focus_proxy ( ) ? * focus_proxy ( ) : * this ;
return window - > focused_widget ( ) = = & effective_focus_widget | | is_ancestor_of ( * window - > focused_widget ( ) ) ;
}
2022-06-12 22:18:52 +02:00
void Widget : : set_shrink_to_fit ( bool shrink_to_fit )
2021-01-04 18:17:14 +01:00
{
2022-06-12 22:18:52 +02:00
// This function is deprecated, and soon to be removed, it is only still here to ease the transition to UIDimensions
if ( shrink_to_fit )
set_preferred_size ( SpecialDimension : : Fit ) ;
2021-01-04 18:17:14 +01:00
}
2021-01-09 00:11:17 +01:00
bool Widget : : has_pending_drop ( ) const
{
return Application : : the ( ) - > pending_drop_widget ( ) = = this ;
}
2021-07-05 18:47:54 +02:00
bool Widget : : is_visible_for_timer_purposes ( ) const
{
2021-07-17 21:36:20 -06:00
return is_visible ( ) & & Object : : is_visible_for_timer_purposes ( ) ;
2021-07-05 18:47:54 +02:00
}
2023-08-13 18:29:05 +02:00
void Widget : : add_spacer ( )
2023-02-16 16:52:56 +00:00
{
VERIFY ( layout ( ) ) ;
2023-08-13 18:29:05 +02:00
return layout ( ) - > add_spacer ( ) ;
2023-02-16 16:52:56 +00:00
}
2023-08-01 20:28:44 +01:00
String Widget : : font_family ( ) const
{
2023-09-05 15:32:52 +02:00
return m_font - > family ( ) ;
2023-08-01 20:28:44 +01:00
}
2020-02-02 15:07:41 +01:00
}