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
*/
2020-02-06 08:07:05 -03:00
# include <LibGfx/Bitmap.h>
2022-04-09 04:28:38 -03:00
# include <LibGfx/Font/Font.h>
2020-02-06 08:04:00 -03:00
# include <LibGfx/StylePainter.h>
2020-05-19 14:02:15 -03:00
# include <WindowServer/Compositor.h>
2020-02-06 16:03:37 -03:00
# include <WindowServer/Event.h>
# include <WindowServer/Screen.h>
# include <WindowServer/WindowManager.h>
# include <WindowServer/WindowSwitcher.h>
namespace WindowServer {
2019-05-11 23:15:25 -03:00
2020-02-06 16:03:37 -03:00
static WindowSwitcher * s_the ;
2019-05-11 23:15:25 -03:00
2020-02-06 16:03:37 -03:00
WindowSwitcher & WindowSwitcher : : the ( )
2019-05-11 23:15:25 -03:00
{
2021-02-23 16:42:32 -03:00
VERIFY ( s_the ) ;
2019-05-11 23:15:25 -03:00
return * s_the ;
}
2019-03-03 11:17:05 -03:00
2020-02-06 16:03:37 -03:00
WindowSwitcher : : WindowSwitcher ( )
2019-03-03 11:17:05 -03:00
{
2019-05-11 23:15:25 -03:00
s_the = this ;
2019-03-03 11:17:05 -03:00
}
2020-02-06 16:03:37 -03:00
void WindowSwitcher : : set_visible ( bool visible )
2019-03-03 11:17:05 -03:00
{
if ( m_visible = = visible )
return ;
m_visible = visible ;
2020-08-18 01:45:10 -03:00
Compositor : : the ( ) . invalidate_occlusions ( ) ;
2019-03-06 06:03:10 -03:00
if ( m_switcher_window )
m_switcher_window - > set_visible ( visible ) ;
if ( ! m_visible )
2019-03-03 11:17:05 -03:00
return ;
2021-11-14 08:00:55 -03:00
clear_hovered_index ( ) ;
2019-03-06 06:03:10 -03:00
refresh ( ) ;
2019-03-03 11:17:05 -03:00
}
2020-02-06 16:03:37 -03:00
Window * WindowSwitcher : : selected_window ( )
2019-03-03 11:17:05 -03:00
{
2020-02-25 10:49:47 -03:00
if ( m_selected_index < 0 | | m_selected_index > = static_cast < int > ( m_windows . size ( ) ) )
2019-03-03 11:17:05 -03:00
return nullptr ;
return m_windows [ m_selected_index ] . ptr ( ) ;
}
2020-02-11 14:28:45 -03:00
void WindowSwitcher : : event ( Core : : Event & event )
{
2021-11-14 08:00:55 -03:00
if ( event . type ( ) = = Event : : WindowLeft ) {
clear_hovered_index ( ) ;
return ;
}
2020-02-11 14:28:45 -03:00
if ( ! static_cast < Event & > ( event ) . is_mouse_event ( ) )
return ;
auto & mouse_event = static_cast < MouseEvent & > ( event ) ;
2020-02-11 14:53:56 -03:00
int new_hovered_index = - 1 ;
2020-02-25 10:49:47 -03:00
for ( size_t i = 0 ; i < m_windows . size ( ) ; + + i ) {
2020-02-11 14:53:56 -03:00
auto item_rect = this - > item_rect ( i ) ;
if ( item_rect . contains ( mouse_event . position ( ) ) ) {
new_hovered_index = i ;
break ;
2020-02-11 14:28:45 -03:00
}
}
2020-02-11 14:53:56 -03:00
if ( mouse_event . type ( ) = = Event : : MouseMove ) {
if ( m_hovered_index ! = new_hovered_index ) {
m_hovered_index = new_hovered_index ;
redraw ( ) ;
}
}
if ( new_hovered_index = = - 1 )
return ;
if ( mouse_event . type ( ) = = Event : : MouseDown )
select_window_at_index ( new_hovered_index ) ;
2020-02-11 14:28:45 -03:00
event . accept ( ) ;
}
2022-04-01 14:58:27 -03:00
void WindowSwitcher : : on_key_event ( KeyEvent const & event )
2019-03-03 11:17:05 -03:00
{
2020-02-06 16:03:37 -03:00
if ( event . type ( ) = = Event : : KeyUp ) {
2021-07-01 23:42:48 -03:00
if ( event . key ( ) = = ( m_mode = = Mode : : ShowAllWindows ? Key_Super : Key_Alt ) ) {
2019-12-28 23:06:04 -03:00
if ( auto * window = selected_window ( ) ) {
2022-08-25 15:41:33 -03:00
WindowManager : : the ( ) . move_to_front_and_make_active ( * window ) ;
2019-12-28 23:06:04 -03:00
}
2020-02-06 16:03:37 -03:00
WindowManager : : the ( ) . set_highlight_window ( nullptr ) ;
2019-03-03 11:17:05 -03:00
hide ( ) ;
}
return ;
}
2020-01-01 14:07:53 -03:00
if ( event . key ( ) = = Key_LeftShift | | event . key ( ) = = Key_RightShift )
return ;
2019-03-03 11:17:05 -03:00
if ( event . key ( ) ! = Key_Tab ) {
2020-02-06 16:03:37 -03:00
WindowManager : : the ( ) . set_highlight_window ( nullptr ) ;
2019-03-03 11:17:05 -03:00
hide ( ) ;
return ;
}
2021-02-23 16:42:32 -03:00
VERIFY ( ! m_windows . is_empty ( ) ) ;
2020-01-01 14:07:53 -03:00
2020-02-11 14:28:45 -03:00
int new_selected_index ;
2020-01-01 14:07:53 -03:00
if ( ! event . shift ( ) ) {
2020-02-25 10:49:47 -03:00
new_selected_index = ( m_selected_index + 1 ) % static_cast < int > ( m_windows . size ( ) ) ;
2020-01-01 14:07:53 -03:00
} else {
2020-02-25 10:49:47 -03:00
new_selected_index = ( m_selected_index - 1 ) % static_cast < int > ( m_windows . size ( ) ) ;
2020-02-11 14:28:45 -03:00
if ( new_selected_index < 0 )
2020-02-25 10:49:47 -03:00
new_selected_index = static_cast < int > ( m_windows . size ( ) ) - 1 ;
2020-01-01 14:07:53 -03:00
}
2021-02-23 16:42:32 -03:00
VERIFY ( new_selected_index < static_cast < int > ( m_windows . size ( ) ) ) ;
2020-02-11 14:28:45 -03:00
select_window_at_index ( new_selected_index ) ;
}
2020-02-11 14:38:48 -03:00
void WindowSwitcher : : select_window ( Window & window )
{
2020-02-25 10:49:47 -03:00
for ( size_t i = 0 ; i < m_windows . size ( ) ; + + i ) {
2020-02-11 14:38:48 -03:00
if ( m_windows . at ( i ) = = & window ) {
select_window_at_index ( i ) ;
return ;
}
}
}
2020-02-11 14:28:45 -03:00
void WindowSwitcher : : select_window_at_index ( int index )
{
m_selected_index = index ;
auto * highlight_window = m_windows . at ( index ) . ptr ( ) ;
2021-02-23 16:42:32 -03:00
VERIFY ( highlight_window ) ;
2021-07-01 23:42:48 -03:00
auto & wm = WindowManager : : the ( ) ;
if ( m_mode = = Mode : : ShowAllWindows ) {
2021-07-02 16:52:23 -03:00
if ( auto & window_stack = highlight_window - > window_stack ( ) ; & window_stack ! = & wm . current_window_stack ( ) )
wm . switch_to_window_stack ( window_stack , nullptr , false ) ;
2021-07-01 23:42:48 -03:00
}
wm . set_highlight_window ( highlight_window ) ;
2020-02-11 14:53:56 -03:00
redraw ( ) ;
}
void WindowSwitcher : : redraw ( )
{
2019-03-06 06:03:10 -03:00
draw ( ) ;
2020-08-18 01:45:10 -03:00
Compositor : : the ( ) . invalidate_screen ( m_rect ) ;
2019-03-03 11:17:05 -03:00
}
2020-06-10 05:57:59 -03:00
Gfx : : IntRect WindowSwitcher : : item_rect ( int index ) const
2020-02-11 14:28:45 -03:00
{
return {
padding ( ) ,
padding ( ) + index * item_height ( ) ,
m_rect . width ( ) - padding ( ) * 2 ,
item_height ( )
} ;
}
2020-02-06 16:03:37 -03:00
void WindowSwitcher : : draw ( )
2019-03-03 11:17:05 -03:00
{
2020-02-06 16:03:37 -03:00
auto palette = WindowManager : : the ( ) . palette ( ) ;
2021-11-14 08:20:18 -03:00
Gfx : : IntRect rect = { { } , m_rect . size ( ) } ;
2020-02-06 07:56:38 -03:00
Gfx : : Painter painter ( * m_switcher_window - > backing_store ( ) ) ;
2021-11-14 08:20:18 -03:00
painter . clear_rect ( rect , Color : : Transparent ) ;
// FIXME: Perhaps the WindowSwitcher could render as an overlay instead.
// That would require adding support for event handling to overlays.
if ( auto * shadow_bitmap = WindowManager : : the ( ) . overlay_rect_shadow ( ) ) {
// FIXME: Support other scale factors.
int scale_factor = 1 ;
Gfx : : StylePainter : : paint_simple_rect_shadow ( painter , rect , shadow_bitmap - > bitmap ( scale_factor ) , true , true ) ;
}
2020-02-25 10:49:47 -03:00
for ( size_t index = 0 ; index < m_windows . size ( ) ; + + index ) {
2021-12-25 06:39:47 -03:00
// FIXME: Ideally we wouldn't be in draw() without having pruned destroyed windows from the list already.
if ( m_windows . at ( index ) = = nullptr )
continue ;
2019-03-03 11:17:05 -03:00
auto & window = * m_windows . at ( index ) ;
2020-02-11 14:28:45 -03:00
auto item_rect = this - > item_rect ( index ) ;
2019-03-03 11:17:05 -03:00
Color text_color ;
Color rect_text_color ;
2020-02-25 10:49:47 -03:00
if ( static_cast < int > ( index ) = = m_selected_index ) {
2019-12-24 16:57:54 -03:00
painter . fill_rect ( item_rect , palette . selection ( ) ) ;
text_color = palette . selection_text ( ) ;
2021-11-14 08:20:18 -03:00
rect_text_color = palette . selection_text ( ) . with_alpha ( 0xcc ) ;
2019-03-03 11:17:05 -03:00
} else {
2020-02-25 10:49:47 -03:00
if ( static_cast < int > ( index ) = = m_hovered_index )
2021-11-14 08:20:18 -03:00
Gfx : : StylePainter : : paint_frame ( painter , item_rect , palette , Gfx : : FrameShape : : Panel , Gfx : : FrameShadow : : Raised , 2 ) ;
text_color = Color : : White ;
rect_text_color = Color ( Color : : White ) . with_alpha ( 0xcc ) ;
2019-03-03 11:17:05 -03:00
}
2019-04-23 17:01:33 -03:00
item_rect . shrink ( item_padding ( ) , 0 ) ;
2020-06-10 05:57:59 -03:00
Gfx : : IntRect thumbnail_rect = { item_rect . location ( ) . translated ( 0 , 5 ) , { thumbnail_width ( ) , thumbnail_height ( ) } } ;
2021-11-14 08:20:18 -03:00
if ( window . backing_store ( ) )
painter . draw_scaled_bitmap ( thumbnail_rect , * window . backing_store ( ) , window . backing_store ( ) - > rect ( ) , 1.0f , Gfx : : Painter : : ScalingMode : : BilinearBlend ) ;
2020-06-10 05:57:59 -03:00
Gfx : : IntRect icon_rect = { thumbnail_rect . bottom_right ( ) . translated ( - window . icon ( ) . width ( ) , - window . icon ( ) . height ( ) ) , { window . icon ( ) . width ( ) , window . icon ( ) . height ( ) } } ;
2019-05-11 23:15:25 -03:00
painter . blit ( icon_rect . location ( ) , window . icon ( ) , window . icon ( ) . rect ( ) ) ;
2021-11-14 08:20:18 -03:00
painter . draw_text ( item_rect . translated ( thumbnail_width ( ) + 12 , 0 ) . translated ( 1 , 1 ) , window . computed_title ( ) , WindowManager : : the ( ) . window_title_font ( ) , Gfx : : TextAlignment : : CenterLeft , text_color . inverted ( ) ) ;
2021-05-09 19:00:40 -03:00
painter . draw_text ( item_rect . translated ( thumbnail_width ( ) + 12 , 0 ) , window . computed_title ( ) , WindowManager : : the ( ) . window_title_font ( ) , Gfx : : TextAlignment : : CenterLeft , text_color ) ;
2021-07-02 16:52:23 -03:00
auto window_details = m_windows_on_multiple_stacks ? String : : formatted ( " {} on {}:{} " , window . rect ( ) . to_string ( ) , window . window_stack ( ) . row ( ) + 1 , window . window_stack ( ) . column ( ) + 1 ) : window . rect ( ) . to_string ( ) ;
2021-07-02 14:02:54 -03:00
painter . draw_text ( item_rect , window_details , Gfx : : TextAlignment : : CenterRight , rect_text_color ) ;
2019-03-03 11:17:05 -03:00
}
}
2020-02-06 16:03:37 -03:00
void WindowSwitcher : : refresh ( )
2019-03-03 11:17:05 -03:00
{
2020-02-06 16:03:37 -03:00
auto & wm = WindowManager : : the ( ) ;
2022-04-01 14:58:27 -03:00
Window const * selected_window = nullptr ;
2019-03-03 11:17:05 -03:00
if ( m_selected_index > 0 & & m_windows [ m_selected_index ] )
selected_window = m_windows [ m_selected_index ] . ptr ( ) ;
2019-05-12 19:08:56 -03:00
if ( ! selected_window )
selected_window = wm . highlight_window ( ) ? wm . highlight_window ( ) : wm . active_window ( ) ;
2019-03-03 11:17:05 -03:00
m_windows . clear ( ) ;
2021-07-02 14:02:54 -03:00
m_windows_on_multiple_stacks = false ;
2019-03-03 11:17:05 -03:00
m_selected_index = 0 ;
int window_count = 0 ;
2019-03-06 07:03:10 -03:00
int longest_title_width = 0 ;
2021-07-01 23:42:48 -03:00
2021-07-02 14:02:54 -03:00
WindowStack * last_added_on_window_stack = nullptr ;
2021-07-01 23:42:48 -03:00
auto add_window_stack_windows = [ & ] ( WindowStack & window_stack ) {
2021-06-29 22:51:26 -03:00
window_stack . for_each_window_of_type_from_front_to_back (
WindowType : : Normal , [ & ] ( Window & window ) {
2022-08-22 19:18:56 -03:00
if ( window . is_frameless ( ) | | window . is_modal ( ) )
2021-06-29 22:51:26 -03:00
return IterationDecision : : Continue ;
+ + window_count ;
longest_title_width = max ( longest_title_width , wm . font ( ) . width ( window . computed_title ( ) ) ) ;
if ( selected_window = = & window )
m_selected_index = m_windows . size ( ) ;
m_windows . append ( window ) ;
2021-07-02 16:52:23 -03:00
auto & window_stack = window . window_stack ( ) ;
2021-07-02 14:02:54 -03:00
if ( ! last_added_on_window_stack ) {
2021-07-02 16:52:23 -03:00
last_added_on_window_stack = & window_stack ;
} else if ( last_added_on_window_stack ! = & window_stack ) {
last_added_on_window_stack = & window_stack ;
2021-07-02 14:02:54 -03:00
m_windows_on_multiple_stacks = true ;
}
2020-05-02 07:15:48 -03:00
return IterationDecision : : Continue ;
2021-06-29 22:51:26 -03:00
} ,
true ) ;
2021-07-01 23:42:48 -03:00
} ;
if ( m_mode = = Mode : : ShowAllWindows ) {
wm . for_each_window_stack ( [ & ] ( auto & window_stack ) {
add_window_stack_windows ( window_stack ) ;
return IterationDecision : : Continue ;
} ) ;
} else {
add_window_stack_windows ( wm . current_window_stack ( ) ) ;
}
2019-03-03 11:17:05 -03:00
if ( m_windows . is_empty ( ) ) {
hide ( ) ;
return ;
}
2021-07-02 14:02:54 -03:00
int space_for_window_details = 200 ;
m_rect . set_width ( thumbnail_width ( ) + longest_title_width + space_for_window_details + padding ( ) * 2 + item_padding ( ) * 2 ) ;
2019-03-03 11:17:05 -03:00
m_rect . set_height ( window_count * item_height ( ) + padding ( ) * 2 ) ;
2021-06-13 09:16:06 -03:00
m_rect . center_within ( Screen : : main ( ) . rect ( ) ) ;
2021-11-14 08:20:18 -03:00
if ( ! m_switcher_window ) {
2020-02-06 16:03:37 -03:00
m_switcher_window = Window : : construct ( * this , WindowType : : WindowSwitcher ) ;
2021-11-14 08:20:18 -03:00
m_switcher_window - > set_has_alpha_channel ( true ) ;
}
2019-03-06 06:03:10 -03:00
m_switcher_window - > set_rect ( m_rect ) ;
2020-02-11 14:53:56 -03:00
redraw ( ) ;
2019-03-06 06:03:10 -03:00
}
2019-05-11 23:15:25 -03:00
2020-02-06 16:03:37 -03:00
void WindowSwitcher : : refresh_if_needed ( )
2019-05-11 23:15:25 -03:00
{
2020-02-11 14:53:56 -03:00
if ( m_visible )
2019-05-11 23:15:25 -03:00
refresh ( ) ;
}
2020-02-06 16:03:37 -03:00
2021-11-14 08:00:55 -03:00
void WindowSwitcher : : clear_hovered_index ( )
{
if ( m_hovered_index = = - 1 )
return ;
m_hovered_index = - 1 ;
redraw ( ) ;
}
2020-02-06 16:03:37 -03:00
}