mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
d815f659cc
This was intentionally enabled with WindowModes as a new Taskbar convenience, but on second thought, it doesn't add up visually. Taskbar buttons show blockers' context menus when available, which is a bit confusing when the window isn't visible. The modeless window's disabled context menu options and inactive title bar also contradict the button. So, this patch reenables the restriction for now. Blocking modals you don't want to answer to immediately can still be tucked away on another workspace.
369 lines
14 KiB
C++
369 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
|
* Copyright (c) 2021, Spencer Dixon <spencercdixon@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include "TaskbarWindow.h"
|
|
#include "ClockWidget.h"
|
|
#include "QuickLaunchWidget.h"
|
|
#include "TaskbarButton.h"
|
|
#include <AK/Debug.h>
|
|
#include <LibCore/StandardPaths.h>
|
|
#include <LibGUI/BoxLayout.h>
|
|
#include <LibGUI/Button.h>
|
|
#include <LibGUI/ConnectionToWindowManagerServer.h>
|
|
#include <LibGUI/ConnectionToWindowServer.h>
|
|
#include <LibGUI/Desktop.h>
|
|
#include <LibGUI/Frame.h>
|
|
#include <LibGUI/Icon.h>
|
|
#include <LibGUI/Menu.h>
|
|
#include <LibGUI/Painter.h>
|
|
#include <LibGUI/Window.h>
|
|
#include <LibGfx/Font/FontDatabase.h>
|
|
#include <LibGfx/Palette.h>
|
|
#include <serenity.h>
|
|
#include <stdio.h>
|
|
|
|
class TaskbarWidget final : public GUI::Widget {
|
|
C_OBJECT(TaskbarWidget);
|
|
|
|
public:
|
|
virtual ~TaskbarWidget() override = default;
|
|
|
|
private:
|
|
TaskbarWidget() = default;
|
|
|
|
virtual void paint_event(GUI::PaintEvent& event) override
|
|
{
|
|
GUI::Painter painter(*this);
|
|
painter.add_clip_rect(event.rect());
|
|
painter.fill_rect(rect(), palette().button());
|
|
painter.draw_line({ 0, 1 }, { width() - 1, 1 }, palette().threed_highlight());
|
|
}
|
|
|
|
virtual void did_layout() override
|
|
{
|
|
WindowList::the().for_each_window([&](auto& window) {
|
|
if (auto* button = window.button())
|
|
static_cast<TaskbarButton*>(button)->update_taskbar_rect();
|
|
});
|
|
}
|
|
};
|
|
|
|
TaskbarWindow::TaskbarWindow(NonnullRefPtr<GUI::Menu> start_menu)
|
|
: m_start_menu(move(start_menu))
|
|
{
|
|
set_window_type(GUI::WindowType::Taskbar);
|
|
set_title("Taskbar");
|
|
|
|
on_screen_rects_change(GUI::Desktop::the().rects(), GUI::Desktop::the().main_screen_index());
|
|
|
|
auto& main_widget = set_main_widget<TaskbarWidget>();
|
|
main_widget.set_layout<GUI::HorizontalBoxLayout>();
|
|
main_widget.layout()->set_margins({ 2, 3, 0, 3 });
|
|
|
|
m_start_button = GUI::Button::construct("Serenity");
|
|
set_start_button_font(Gfx::FontDatabase::default_font().bold_variant());
|
|
m_start_button->set_icon_spacing(0);
|
|
auto app_icon = GUI::Icon::default_icon("ladyball"sv);
|
|
m_start_button->set_icon(app_icon.bitmap_for_size(16));
|
|
m_start_button->set_menu(m_start_menu);
|
|
|
|
main_widget.add_child(*m_start_button);
|
|
main_widget.add<Taskbar::QuickLaunchWidget>();
|
|
|
|
m_task_button_container = main_widget.add<GUI::Widget>();
|
|
m_task_button_container->set_layout<GUI::HorizontalBoxLayout>();
|
|
m_task_button_container->layout()->set_spacing(3);
|
|
|
|
m_default_icon = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/window.png"sv).release_value_but_fixme_should_propagate_errors();
|
|
|
|
m_applet_area_container = main_widget.add<GUI::Frame>();
|
|
m_applet_area_container->set_frame_thickness(1);
|
|
m_applet_area_container->set_frame_shape(Gfx::FrameShape::Box);
|
|
m_applet_area_container->set_frame_shadow(Gfx::FrameShadow::Sunken);
|
|
|
|
m_clock_widget = main_widget.add<Taskbar::ClockWidget>();
|
|
|
|
m_show_desktop_button = GUI::Button::construct();
|
|
m_show_desktop_button->set_tooltip("Show Desktop");
|
|
m_show_desktop_button->set_icon(GUI::Icon::default_icon("desktop"sv).bitmap_for_size(16));
|
|
m_show_desktop_button->set_button_style(Gfx::ButtonStyle::Coolbar);
|
|
m_show_desktop_button->set_fixed_size(24, 24);
|
|
m_show_desktop_button->on_click = TaskbarWindow::show_desktop_button_clicked;
|
|
main_widget.add_child(*m_show_desktop_button);
|
|
|
|
auto af_path = String::formatted("{}/{}", Desktop::AppFile::APP_FILES_DIRECTORY, "Assistant.af");
|
|
m_assistant_app_file = Desktop::AppFile::open(af_path);
|
|
}
|
|
|
|
void TaskbarWindow::config_string_did_change(String const& domain, String const& group, String const& key, String const& value)
|
|
{
|
|
VERIFY(domain == "Taskbar");
|
|
if (group == "Clock" && key == "TimeFormat") {
|
|
m_clock_widget->update_format(value);
|
|
update_applet_area();
|
|
}
|
|
}
|
|
|
|
void TaskbarWindow::show_desktop_button_clicked(unsigned)
|
|
{
|
|
toggle_show_desktop();
|
|
}
|
|
|
|
void TaskbarWindow::toggle_show_desktop()
|
|
{
|
|
GUI::ConnectionToWindowManagerServer::the().async_toggle_show_desktop();
|
|
}
|
|
|
|
void TaskbarWindow::on_screen_rects_change(Vector<Gfx::IntRect, 4> const& rects, size_t main_screen_index)
|
|
{
|
|
auto const& rect = rects[main_screen_index];
|
|
Gfx::IntRect new_rect { rect.x(), rect.bottom() - taskbar_height() + 1, rect.width(), taskbar_height() };
|
|
set_rect(new_rect);
|
|
update_applet_area();
|
|
}
|
|
|
|
void TaskbarWindow::update_applet_area()
|
|
{
|
|
// NOTE: Widget layout is normally lazy, but here we have to force it right away so we can tell
|
|
// WindowServer where to place the applet area window.
|
|
if (!main_widget())
|
|
return;
|
|
main_widget()->do_layout();
|
|
auto new_rect = Gfx::IntRect({}, m_applet_area_size).centered_within(m_applet_area_container->screen_relative_rect());
|
|
GUI::ConnectionToWindowManagerServer::the().async_set_applet_area_position(new_rect.location());
|
|
}
|
|
|
|
NonnullRefPtr<GUI::Button> TaskbarWindow::create_button(WindowIdentifier const& identifier)
|
|
{
|
|
auto& button = m_task_button_container->add<TaskbarButton>(identifier);
|
|
button.set_min_size(20, 21);
|
|
button.set_max_size(140, 21);
|
|
button.set_text_alignment(Gfx::TextAlignment::CenterLeft);
|
|
button.set_icon(*m_default_icon);
|
|
return button;
|
|
}
|
|
|
|
void TaskbarWindow::add_window_button(::Window& window, WindowIdentifier const& identifier)
|
|
{
|
|
if (window.button())
|
|
return;
|
|
window.set_button(create_button(identifier));
|
|
auto* button = window.button();
|
|
button->on_click = [window = &window, identifier](auto) {
|
|
if (window->is_minimized() || !window->is_active())
|
|
GUI::ConnectionToWindowManagerServer::the().async_set_active_window(identifier.client_id(), identifier.window_id());
|
|
else if (!window->is_blocked())
|
|
GUI::ConnectionToWindowManagerServer::the().async_set_window_minimized(identifier.client_id(), identifier.window_id(), true);
|
|
};
|
|
}
|
|
|
|
void TaskbarWindow::remove_window_button(::Window& window, bool was_removed)
|
|
{
|
|
auto* button = window.button();
|
|
if (!button)
|
|
return;
|
|
if (!was_removed)
|
|
static_cast<TaskbarButton*>(button)->clear_taskbar_rect();
|
|
window.set_button(nullptr);
|
|
button->remove_from_parent();
|
|
}
|
|
|
|
void TaskbarWindow::update_window_button(::Window& window, bool show_as_active)
|
|
{
|
|
auto* button = window.button();
|
|
if (!button)
|
|
return;
|
|
button->set_text(window.title());
|
|
button->set_tooltip(window.title());
|
|
button->set_checked(show_as_active);
|
|
button->set_visible(is_window_on_current_workspace(window));
|
|
}
|
|
|
|
void TaskbarWindow::event(Core::Event& event)
|
|
{
|
|
switch (event.type()) {
|
|
case GUI::Event::MouseDown: {
|
|
// If the cursor is at the edge/corner of the screen but technically not within the start button (or other taskbar buttons),
|
|
// we adjust it so that the nearest button ends up being clicked anyways.
|
|
|
|
auto& mouse_event = static_cast<GUI::MouseEvent&>(event);
|
|
int const ADJUSTMENT = 4;
|
|
auto adjusted_x = AK::clamp(mouse_event.x(), ADJUSTMENT, width() - ADJUSTMENT);
|
|
auto adjusted_y = AK::min(mouse_event.y(), height() - ADJUSTMENT);
|
|
Gfx::IntPoint adjusted_point = { adjusted_x, adjusted_y };
|
|
|
|
if (adjusted_point != mouse_event.position()) {
|
|
GUI::ConnectionToWindowServer::the().async_set_global_cursor_position(position() + adjusted_point);
|
|
GUI::MouseEvent adjusted_event = { (GUI::Event::Type)mouse_event.type(), adjusted_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta_x(), mouse_event.wheel_delta_y(), mouse_event.wheel_raw_delta_x(), mouse_event.wheel_raw_delta_y() };
|
|
Window::event(adjusted_event);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case GUI::Event::FontsChange:
|
|
set_start_button_font(Gfx::FontDatabase::default_font().bold_variant());
|
|
break;
|
|
}
|
|
Window::event(event);
|
|
}
|
|
|
|
void TaskbarWindow::wm_event(GUI::WMEvent& event)
|
|
{
|
|
WindowIdentifier identifier { event.client_id(), event.window_id() };
|
|
switch (event.type()) {
|
|
case GUI::Event::WM_WindowRemoved: {
|
|
if constexpr (EVENT_DEBUG) {
|
|
auto& removed_event = static_cast<GUI::WMWindowRemovedEvent&>(event);
|
|
dbgln("WM_WindowRemoved: client_id={}, window_id={}",
|
|
removed_event.client_id(),
|
|
removed_event.window_id());
|
|
}
|
|
if (auto* window = WindowList::the().window(identifier))
|
|
remove_window_button(*window, true);
|
|
WindowList::the().remove_window(identifier);
|
|
update();
|
|
break;
|
|
}
|
|
case GUI::Event::WM_WindowRectChanged: {
|
|
if constexpr (EVENT_DEBUG) {
|
|
auto& changed_event = static_cast<GUI::WMWindowRectChangedEvent&>(event);
|
|
dbgln("WM_WindowRectChanged: client_id={}, window_id={}, rect={}",
|
|
changed_event.client_id(),
|
|
changed_event.window_id(),
|
|
changed_event.rect());
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GUI::Event::WM_WindowIconBitmapChanged: {
|
|
auto& changed_event = static_cast<GUI::WMWindowIconBitmapChangedEvent&>(event);
|
|
if (auto* window = WindowList::the().window(identifier)) {
|
|
if (window->button()) {
|
|
auto icon = changed_event.bitmap();
|
|
if (icon->height() != taskbar_icon_size() || icon->width() != taskbar_icon_size()) {
|
|
auto sw = taskbar_icon_size() / (float)icon->width();
|
|
auto sh = taskbar_icon_size() / (float)icon->height();
|
|
auto scaled_bitmap_or_error = icon->scaled(sw, sh);
|
|
if (scaled_bitmap_or_error.is_error())
|
|
window->button()->set_icon(nullptr);
|
|
else
|
|
window->button()->set_icon(scaled_bitmap_or_error.release_value());
|
|
} else {
|
|
window->button()->set_icon(icon);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GUI::Event::WM_WindowStateChanged: {
|
|
auto& changed_event = static_cast<GUI::WMWindowStateChangedEvent&>(event);
|
|
if constexpr (EVENT_DEBUG) {
|
|
dbgln("WM_WindowStateChanged: client_id={}, window_id={}, title={}, rect={}, is_active={}, is_blocked={}, is_minimized={}",
|
|
changed_event.client_id(),
|
|
changed_event.window_id(),
|
|
changed_event.title(),
|
|
changed_event.rect(),
|
|
changed_event.is_active(),
|
|
changed_event.is_blocked(),
|
|
changed_event.is_minimized());
|
|
}
|
|
if (changed_event.window_type() != GUI::WindowType::Normal || changed_event.is_frameless()) {
|
|
if (auto* window = WindowList::the().window(identifier))
|
|
remove_window_button(*window, false);
|
|
break;
|
|
}
|
|
auto& window = WindowList::the().ensure_window(identifier);
|
|
window.set_title(changed_event.title());
|
|
window.set_rect(changed_event.rect());
|
|
window.set_active(changed_event.is_active());
|
|
window.set_blocked(changed_event.is_blocked());
|
|
window.set_minimized(changed_event.is_minimized());
|
|
window.set_progress(changed_event.progress());
|
|
window.set_workspace(changed_event.workspace_row(), changed_event.workspace_column());
|
|
add_window_button(window, identifier);
|
|
update_window_button(window, window.is_active());
|
|
break;
|
|
}
|
|
case GUI::Event::WM_AppletAreaSizeChanged: {
|
|
auto& changed_event = static_cast<GUI::WMAppletAreaSizeChangedEvent&>(event);
|
|
m_applet_area_size = changed_event.size();
|
|
m_applet_area_container->set_fixed_size(changed_event.size().width() + 8, 21);
|
|
update_applet_area();
|
|
break;
|
|
}
|
|
case GUI::Event::WM_SuperKeyPressed: {
|
|
if (m_start_menu->is_visible()) {
|
|
m_start_menu->dismiss();
|
|
} else {
|
|
m_start_menu->popup(m_start_button->screen_relative_rect().top_left());
|
|
}
|
|
break;
|
|
}
|
|
case GUI::Event::WM_SuperSpaceKeyPressed: {
|
|
if (!m_assistant_app_file->spawn())
|
|
warnln("failed to spawn 'Assistant' when requested via Super+Space");
|
|
break;
|
|
}
|
|
case GUI::Event::WM_SuperDKeyPressed: {
|
|
toggle_show_desktop();
|
|
break;
|
|
}
|
|
case GUI::Event::WM_SuperDigitKeyPressed: {
|
|
auto& digit_event = static_cast<GUI::WMSuperDigitKeyPressedEvent&>(event);
|
|
auto index = digit_event.digit() != 0 ? digit_event.digit() - 1 : 9;
|
|
|
|
for (auto& widget : m_task_button_container->child_widgets()) {
|
|
// NOTE: The button might be invisible depending on the current workspace
|
|
if (!widget.is_visible())
|
|
continue;
|
|
|
|
if (index == 0) {
|
|
static_cast<TaskbarButton&>(widget).click();
|
|
break;
|
|
}
|
|
|
|
--index;
|
|
}
|
|
break;
|
|
}
|
|
case GUI::Event::WM_WorkspaceChanged: {
|
|
auto& changed_event = static_cast<GUI::WMWorkspaceChangedEvent&>(event);
|
|
workspace_change_event(changed_event.current_row(), changed_event.current_column());
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TaskbarWindow::screen_rects_change_event(GUI::ScreenRectsChangeEvent& event)
|
|
{
|
|
on_screen_rects_change(event.rects(), event.main_screen_index());
|
|
}
|
|
|
|
bool TaskbarWindow::is_window_on_current_workspace(::Window& window) const
|
|
{
|
|
return window.workspace_row() == m_current_workspace_row && window.workspace_column() == m_current_workspace_column;
|
|
}
|
|
|
|
void TaskbarWindow::workspace_change_event(unsigned current_row, unsigned current_column)
|
|
{
|
|
m_current_workspace_row = current_row;
|
|
m_current_workspace_column = current_column;
|
|
|
|
WindowList::the().for_each_window([&](auto& window) {
|
|
if (auto* button = window.button())
|
|
button->set_visible(is_window_on_current_workspace(window));
|
|
});
|
|
}
|
|
|
|
void TaskbarWindow::set_start_button_font(Gfx::Font const& font)
|
|
{
|
|
m_start_button->set_font(font);
|
|
m_start_button->set_fixed_size(font.width(m_start_button->text()) + 30, 21);
|
|
}
|