diff options
Diffstat (limited to 'Userland/Services/Taskbar')
-rw-r--r-- | Userland/Services/Taskbar/CMakeLists.txt | 9 | ||||
-rw-r--r-- | Userland/Services/Taskbar/TaskbarButton.cpp | 162 | ||||
-rw-r--r-- | Userland/Services/Taskbar/TaskbarButton.h | 48 | ||||
-rw-r--r-- | Userland/Services/Taskbar/TaskbarWindow.cpp | 314 | ||||
-rw-r--r-- | Userland/Services/Taskbar/TaskbarWindow.h | 53 | ||||
-rw-r--r-- | Userland/Services/Taskbar/WindowIdentifier.h | 63 | ||||
-rw-r--r-- | Userland/Services/Taskbar/WindowList.cpp | 71 | ||||
-rw-r--r-- | Userland/Services/Taskbar/WindowList.h | 115 | ||||
-rw-r--r-- | Userland/Services/Taskbar/main.cpp | 57 |
9 files changed, 892 insertions, 0 deletions
diff --git a/Userland/Services/Taskbar/CMakeLists.txt b/Userland/Services/Taskbar/CMakeLists.txt new file mode 100644 index 0000000000..83de9ee9c8 --- /dev/null +++ b/Userland/Services/Taskbar/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCES + main.cpp + TaskbarButton.cpp + TaskbarWindow.cpp + WindowList.cpp +) + +serenity_bin(Taskbar) +target_link_libraries(Taskbar LibGUI LibDesktop) diff --git a/Userland/Services/Taskbar/TaskbarButton.cpp b/Userland/Services/Taskbar/TaskbarButton.cpp new file mode 100644 index 0000000000..69103ca5e0 --- /dev/null +++ b/Userland/Services/Taskbar/TaskbarButton.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "TaskbarButton.h" +#include "WindowList.h" +#include <LibGUI/Action.h> +#include <LibGUI/Painter.h> +#include <LibGUI/WindowServerConnection.h> +#include <LibGfx/Font.h> +#include <LibGfx/FontDatabase.h> +#include <LibGfx/Palette.h> +#include <LibGfx/StylePainter.h> + +TaskbarButton::TaskbarButton(const WindowIdentifier& identifier) + : m_identifier(identifier) +{ +} + +TaskbarButton::~TaskbarButton() +{ +} + +void TaskbarButton::context_menu_event(GUI::ContextMenuEvent&) +{ + GUI::WindowServerConnection::the().post_message(Messages::WindowServer::WM_PopupWindowMenu(m_identifier.client_id(), m_identifier.window_id(), screen_relative_rect().location())); +} + +void TaskbarButton::update_taskbar_rect() +{ + GUI::WindowServerConnection::the().post_message( + Messages::WindowServer::WM_SetWindowTaskbarRect( + m_identifier.client_id(), + m_identifier.window_id(), + screen_relative_rect())); +} + +void TaskbarButton::clear_taskbar_rect() +{ + GUI::WindowServerConnection::the().post_message( + Messages::WindowServer::WM_SetWindowTaskbarRect( + m_identifier.client_id(), + m_identifier.window_id(), + {})); +} + +void TaskbarButton::resize_event(GUI::ResizeEvent& event) +{ + update_taskbar_rect(); + return GUI::Button::resize_event(event); +} + +static void paint_custom_progress_bar(GUI::Painter& painter, const Gfx::IntRect& rect, const Gfx::IntRect& text_rect, const Palette& palette, int min, int max, int value, const StringView& text, const Gfx::Font& font, Gfx::TextAlignment text_alignment) +{ + float range_size = max - min; + float progress = (value - min) / range_size; + float progress_width = progress * rect.width(); + + Gfx::IntRect progress_rect { rect.x(), rect.y(), (int)progress_width, rect.height() }; + + { + Gfx::PainterStateSaver saver(painter); + painter.add_clip_rect(progress_rect); + + Color start_color = palette.active_window_border1(); + Color end_color = palette.active_window_border2(); + painter.fill_rect_with_gradient(rect, start_color, end_color); + + if (!text.is_null()) { + painter.draw_text(text_rect.translated(1, 1), text, font, text_alignment, palette.base_text(), Gfx::TextElision::Right); + painter.draw_text(text_rect, text, font, text_alignment, palette.base_text().inverted(), Gfx::TextElision::Right); + } + } + + Gfx::IntRect hole_rect { (int)progress_width, 0, (int)(rect.width() - progress_width), rect.height() }; + hole_rect.move_by(rect.location()); + hole_rect.set_right_without_resize(rect.right()); + Gfx::PainterStateSaver saver(painter); + painter.add_clip_rect(hole_rect); + if (!text.is_null()) + painter.draw_text(text_rect, text, font, text_alignment, palette.base_text(), Gfx::TextElision::Right); +} + +void TaskbarButton::paint_event(GUI::PaintEvent& event) +{ + ASSERT(icon()); + auto& icon = *this->icon(); + auto& font = is_checked() ? Gfx::FontDatabase::default_bold_font() : this->font(); + auto& window = WindowList::the().ensure_window(m_identifier); + + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + + Gfx::StylePainter::paint_button(painter, rect(), palette(), button_style(), is_being_pressed(), is_hovered(), is_checked(), is_enabled()); + + if (text().is_empty()) + return; + + bool has_progress = window.progress() >= 0 && window.progress() <= 100; + + auto content_rect = rect().shrunken(8, 2); + auto icon_location = content_rect.center().translated(-(icon.width() / 2), -(icon.height() / 2)); + if (!text().is_empty()) + icon_location.set_x(content_rect.x()); + + if (!text().is_empty()) { + content_rect.move_by(icon.width() + 4, 0); + content_rect.set_width(content_rect.width() - icon.width() - 4); + } + + Gfx::IntRect text_rect { 0, 0, font.width(text()), font.glyph_height() }; + if (text_rect.width() > content_rect.width()) + text_rect.set_width(content_rect.width()); + text_rect.align_within(content_rect, text_alignment()); + + if (is_being_pressed() || is_checked()) { + text_rect.move_by(1, 1); + icon_location.move_by(1, 1); + } + + if (has_progress) { + auto adjusted_rect = rect().shrunken(4, 4); + if (is_being_pressed() || is_checked()) { + adjusted_rect.set_height(adjusted_rect.height() + 1); + } + paint_custom_progress_bar(painter, adjusted_rect, text_rect, palette(), 0, 100, window.progress(), text(), font, text_alignment()); + } + + if (is_enabled()) { + if (is_hovered()) + painter.blit_brightened(icon_location, icon, icon.rect()); + else + painter.blit(icon_location, icon, icon.rect()); + } else { + painter.blit_disabled(icon_location, icon, icon.rect(), palette()); + } + + if (!has_progress) + paint_text(painter, text_rect, font, text_alignment()); +} diff --git a/Userland/Services/Taskbar/TaskbarButton.h b/Userland/Services/Taskbar/TaskbarButton.h new file mode 100644 index 0000000000..b990c04d03 --- /dev/null +++ b/Userland/Services/Taskbar/TaskbarButton.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "WindowIdentifier.h" +#include <LibGUI/Button.h> + +class TaskbarButton final : public GUI::Button { + C_OBJECT(TaskbarButton) +public: + virtual ~TaskbarButton() override; + + void update_taskbar_rect(); + void clear_taskbar_rect(); + +private: + explicit TaskbarButton(const WindowIdentifier&); + + virtual void context_menu_event(GUI::ContextMenuEvent&) override; + virtual void resize_event(GUI::ResizeEvent&) override; + virtual void paint_event(GUI::PaintEvent&) override; + + WindowIdentifier m_identifier; +}; diff --git a/Userland/Services/Taskbar/TaskbarWindow.cpp b/Userland/Services/Taskbar/TaskbarWindow.cpp new file mode 100644 index 0000000000..fbc7946634 --- /dev/null +++ b/Userland/Services/Taskbar/TaskbarWindow.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "TaskbarWindow.h" +#include "TaskbarButton.h" +#include <AK/SharedBuffer.h> +#include <LibCore/ConfigFile.h> +#include <LibCore/StandardPaths.h> +#include <LibDesktop/AppFile.h> +#include <LibGUI/BoxLayout.h> +#include <LibGUI/Button.h> +#include <LibGUI/Desktop.h> +#include <LibGUI/Frame.h> +#include <LibGUI/Icon.h> +#include <LibGUI/Painter.h> +#include <LibGUI/Window.h> +#include <LibGUI/WindowServerConnection.h> +#include <LibGfx/Palette.h> +#include <serenity.h> +#include <stdio.h> + +//#define EVENT_DEBUG + +class TaskbarWidget final : public GUI::Widget { + C_OBJECT(TaskbarWidget); + +public: + virtual ~TaskbarWidget() override { } + +private: + TaskbarWidget() { } + + 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() +{ + set_window_type(GUI::WindowType::Taskbar); + set_title("Taskbar"); + + on_screen_rect_change(GUI::Desktop::the().rect()); + + GUI::Desktop::the().on_rect_change = [this](const Gfx::IntRect& rect) { on_screen_rect_change(rect); }; + + auto& widget = set_main_widget<TaskbarWidget>(); + widget.set_layout<GUI::HorizontalBoxLayout>(); + widget.layout()->set_margins({ 3, 2, 3, 2 }); + widget.layout()->set_spacing(3); + + m_default_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png"); + + create_quick_launch_bar(); +} + +TaskbarWindow::~TaskbarWindow() +{ +} + +void TaskbarWindow::create_quick_launch_bar() +{ + auto& quick_launch_bar = main_widget()->add<GUI::Frame>(); + quick_launch_bar.set_layout<GUI::HorizontalBoxLayout>(); + quick_launch_bar.layout()->set_spacing(0); + quick_launch_bar.layout()->set_margins({ 3, 0, 3, 0 }); + quick_launch_bar.set_frame_thickness(0); + + int total_width = 6; + bool first = true; + + auto config = Core::ConfigFile::get_for_app("Taskbar"); + constexpr const char* quick_launch = "QuickLaunch"; + + // FIXME: Core::ConfigFile does not keep the order of the entries. + for (auto& name : config->keys(quick_launch)) { + auto af_name = config->read_entry(quick_launch, name); + auto af_path = String::formatted("{}/{}", Desktop::AppFile::APP_FILES_DIRECTORY, af_name); + auto af = Desktop::AppFile::open(af_path); + if (!af->is_valid()) + continue; + auto app_executable = af->executable(); + const int button_size = 24; + auto& button = quick_launch_bar.add<GUI::Button>(); + button.set_fixed_size(button_size, button_size); + button.set_button_style(Gfx::ButtonStyle::CoolBar); + button.set_icon(af->icon().bitmap_for_size(16)); + button.set_tooltip(af->name()); + button.on_click = [app_executable](auto) { + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + } else if (pid == 0) { + if (chdir(Core::StandardPaths::home_directory().characters()) < 0) { + perror("chdir"); + exit(1); + } + execl(app_executable.characters(), app_executable.characters(), nullptr); + perror("execl"); + ASSERT_NOT_REACHED(); + } else { + if (disown(pid) < 0) + perror("disown"); + } + }; + + if (!first) + total_width += quick_launch_bar.layout()->spacing(); + first = false; + total_width += button_size; + } + + quick_launch_bar.set_fixed_size(total_width, 24); +} + +void TaskbarWindow::on_screen_rect_change(const Gfx::IntRect& rect) +{ + Gfx::IntRect new_rect { rect.x(), rect.bottom() - taskbar_height() + 1, rect.width(), taskbar_height() }; + set_rect(new_rect); +} + +NonnullRefPtr<GUI::Button> TaskbarWindow::create_button(const WindowIdentifier& identifier) +{ + auto& button = main_widget()->add<TaskbarButton>(identifier); + button.set_min_size(20, 23); + button.set_max_size(140, 23); + button.set_text_alignment(Gfx::TextAlignment::CenterLeft); + button.set_icon(*m_default_icon); + return button; +} + +static bool should_include_window(GUI::WindowType window_type, bool is_frameless) +{ + return window_type == GUI::WindowType::Normal && !is_frameless; +} + +void TaskbarWindow::add_window_button(::Window& window, const WindowIdentifier& identifier) +{ + if (window.button()) + return; + window.set_button(create_button(identifier)); + auto* button = window.button(); + button->on_click = [window = &window, identifier, button](auto) { + // We need to look at the button's checked state here to figure + // out if the application is active or not. That's because this + // button's window may not actually be active when a modal window + // is displayed, in which case window->is_active() would return + // false because window is the modal window's owner (which is not + // active) + if (window->is_minimized() || !button->is_checked()) { + GUI::WindowServerConnection::the().post_message(Messages::WindowServer::WM_SetActiveWindow(identifier.client_id(), identifier.window_id())); + } else { + GUI::WindowServerConnection::the().post_message(Messages::WindowServer::WM_SetWindowMinimized(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_checked(show_as_active); +} + +::Window* TaskbarWindow::find_window_owner(::Window& window) const +{ + if (!window.is_modal()) + return &window; + + ::Window* parent = nullptr; + auto* current_window = &window; + while (current_window) { + parent = WindowList::the().find_parent(*current_window); + if (!parent || !parent->is_modal()) + break; + current_window = parent; + } + return parent; +} + +void TaskbarWindow::wm_event(GUI::WMEvent& event) +{ + WindowIdentifier identifier { event.client_id(), event.window_id() }; + switch (event.type()) { + case GUI::Event::WM_WindowRemoved: { +#ifdef 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()); +#endif + if (auto* window = WindowList::the().window(identifier)) + remove_window_button(*window, true); + WindowList::the().remove_window(identifier); + update(); + break; + } + case GUI::Event::WM_WindowRectChanged: { +#ifdef 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()); +#endif + break; + } + + case GUI::Event::WM_WindowIconBitmapChanged: { + auto& changed_event = static_cast<GUI::WMWindowIconBitmapChangedEvent&>(event); +#ifdef EVENT_DEBUG + dbgln("WM_WindowIconBitmapChanged: client_id={}, window_id={}, icon_buffer_id={}", + changed_event.client_id(), + changed_event.window_id(), + changed_event.icon_buffer_id()); +#endif + if (auto* window = WindowList::the().window(identifier)) { + auto buffer = SharedBuffer::create_from_shbuf_id(changed_event.icon_buffer_id()); + ASSERT(buffer); + if (window->button()) + window->button()->set_icon(Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, *buffer, changed_event.icon_size())); + } + break; + } + + case GUI::Event::WM_WindowStateChanged: { + auto& changed_event = static_cast<GUI::WMWindowStateChangedEvent&>(event); +#ifdef EVENT_DEBUG + dbgln("WM_WindowStateChanged: client_id={}, window_id={}, title={}, rect={}, is_active={}, is_minimized={}", + changed_event.client_id(), + changed_event.window_id(), + changed_event.title(), + changed_event.rect(), + changed_event.is_active(), + changed_event.is_minimized()); +#endif + if (!should_include_window(changed_event.window_type(), changed_event.is_frameless())) + break; + auto& window = WindowList::the().ensure_window(identifier); + window.set_parent_identifier({ changed_event.parent_client_id(), changed_event.parent_window_id() }); + if (!window.is_modal()) + add_window_button(window, identifier); + else + remove_window_button(window, false); + window.set_title(changed_event.title()); + window.set_rect(changed_event.rect()); + window.set_modal(changed_event.is_modal()); + window.set_active(changed_event.is_active()); + window.set_minimized(changed_event.is_minimized()); + window.set_progress(changed_event.progress()); + + auto* window_owner = find_window_owner(window); + if (window_owner == &window) { + update_window_button(window, window.is_active()); + } else if (window_owner) { + // check the window owner's button if the modal's window button + // would have been checked + ASSERT(window.is_modal()); + update_window_button(*window_owner, window.is_active()); + } + break; + } + default: + break; + } +} diff --git a/Userland/Services/Taskbar/TaskbarWindow.h b/Userland/Services/Taskbar/TaskbarWindow.h new file mode 100644 index 0000000000..be55a00daf --- /dev/null +++ b/Userland/Services/Taskbar/TaskbarWindow.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "WindowList.h" +#include <LibGUI/Widget.h> +#include <LibGUI/Window.h> + +class TaskbarWindow final : public GUI::Window { + C_OBJECT(TaskbarWindow) +public: + TaskbarWindow(); + virtual ~TaskbarWindow() override; + + int taskbar_height() const { return 28; } + +private: + void create_quick_launch_bar(); + void on_screen_rect_change(const Gfx::IntRect&); + NonnullRefPtr<GUI::Button> create_button(const WindowIdentifier&); + void add_window_button(::Window&, const WindowIdentifier&); + void remove_window_button(::Window&, bool); + void update_window_button(::Window&, bool); + ::Window* find_window_owner(::Window&) const; + + virtual void wm_event(GUI::WMEvent&) override; + + RefPtr<Gfx::Bitmap> m_default_icon; +}; diff --git a/Userland/Services/Taskbar/WindowIdentifier.h b/Userland/Services/Taskbar/WindowIdentifier.h new file mode 100644 index 0000000000..c6e996992c --- /dev/null +++ b/Userland/Services/Taskbar/WindowIdentifier.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/Traits.h> + +class WindowIdentifier { +public: + WindowIdentifier() = default; + WindowIdentifier(int client_id, int window_id) + : m_client_id(client_id) + , m_window_id(window_id) + { + } + + int client_id() const { return m_client_id; } + int window_id() const { return m_window_id; } + + bool operator==(const WindowIdentifier& other) const + { + return m_client_id == other.m_client_id && m_window_id == other.m_window_id; + } + + bool is_valid() const + { + return m_client_id != -1 && m_window_id != -1; + } + +private: + int m_client_id { -1 }; + int m_window_id { -1 }; +}; + +namespace AK { +template<> +struct Traits<WindowIdentifier> : public GenericTraits<WindowIdentifier> { + static unsigned hash(const WindowIdentifier& w) { return pair_int_hash(w.client_id(), w.window_id()); } +}; +} diff --git a/Userland/Services/Taskbar/WindowList.cpp b/Userland/Services/Taskbar/WindowList.cpp new file mode 100644 index 0000000000..84021be160 --- /dev/null +++ b/Userland/Services/Taskbar/WindowList.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "WindowList.h" + +WindowList& WindowList::the() +{ + static WindowList* s_the; + if (!s_the) + s_the = new WindowList; + return *s_the; +} + +Window* WindowList::find_parent(const Window& window) +{ + if (!window.parent_identifier().is_valid()) + return nullptr; + for (auto& it : m_windows) { + auto& w = *it.value; + if (w.identifier() == window.parent_identifier()) + return &w; + } + return nullptr; +} + +Window* WindowList::window(const WindowIdentifier& identifier) +{ + auto it = m_windows.find(identifier); + if (it != m_windows.end()) + return it->value; + return nullptr; +} + +Window& WindowList::ensure_window(const WindowIdentifier& identifier) +{ + auto it = m_windows.find(identifier); + if (it != m_windows.end()) + return *it->value; + auto window = make<Window>(identifier); + auto& window_ref = *window; + m_windows.set(identifier, move(window)); + return window_ref; +} + +void WindowList::remove_window(const WindowIdentifier& identifier) +{ + m_windows.remove(identifier); +} diff --git a/Userland/Services/Taskbar/WindowList.h b/Userland/Services/Taskbar/WindowList.h new file mode 100644 index 0000000000..b86b509926 --- /dev/null +++ b/Userland/Services/Taskbar/WindowList.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "WindowIdentifier.h" +#include <AK/HashMap.h> +#include <AK/String.h> +#include <LibGUI/Button.h> +#include <LibGfx/Rect.h> + +class Window { +public: + explicit Window(const WindowIdentifier& identifier) + : m_identifier(identifier) + { + } + + ~Window() + { + if (m_button) + m_button->remove_from_parent(); + } + + const WindowIdentifier& identifier() const { return m_identifier; } + + void set_parent_identifier(const WindowIdentifier& parent_identifier) { m_parent_identifier = parent_identifier; } + const WindowIdentifier& parent_identifier() const { return m_parent_identifier; } + + String title() const { return m_title; } + void set_title(const String& title) { m_title = title; } + + Gfx::IntRect rect() const { return m_rect; } + void set_rect(const Gfx::IntRect& rect) { m_rect = rect; } + + GUI::Button* button() { return m_button; } + void set_button(GUI::Button* button) { m_button = button; } + + void set_active(bool active) { m_active = active; } + bool is_active() const { return m_active; } + + void set_minimized(bool minimized) { m_minimized = minimized; } + bool is_minimized() const { return m_minimized; } + + void set_modal(bool modal) { m_modal = modal; } + bool is_modal() const { return m_modal; } + + void set_progress(int progress) + { + if (m_progress == progress) + return; + m_progress = progress; + if (m_button) + m_button->update(); + } + + int progress() const { return m_progress; } + + const Gfx::Bitmap* icon() const { return m_icon.ptr(); } + +private: + WindowIdentifier m_identifier; + WindowIdentifier m_parent_identifier; + String m_title; + Gfx::IntRect m_rect; + RefPtr<GUI::Button> m_button; + RefPtr<Gfx::Bitmap> m_icon; + bool m_active { false }; + bool m_minimized { false }; + bool m_modal { false }; + int m_progress { -1 }; +}; + +class WindowList { +public: + static WindowList& the(); + + template<typename Callback> + void for_each_window(Callback callback) + { + for (auto& it : m_windows) + callback(*it.value); + } + + Window* find_parent(const Window&); + Window* window(const WindowIdentifier&); + Window& ensure_window(const WindowIdentifier&); + void remove_window(const WindowIdentifier&); + +private: + HashMap<WindowIdentifier, NonnullOwnPtr<Window>> m_windows; +}; diff --git a/Userland/Services/Taskbar/main.cpp b/Userland/Services/Taskbar/main.cpp new file mode 100644 index 0000000000..3917285497 --- /dev/null +++ b/Userland/Services/Taskbar/main.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "TaskbarWindow.h" +#include <LibCore/EventLoop.h> +#include <LibGUI/Application.h> +#include <signal.h> +#include <stdio.h> +#include <sys/wait.h> + +int main(int argc, char** argv) +{ + if (pledge("stdio shared_buffer accept proc exec rpath unix cpath fattr sigaction", nullptr) < 0) { + perror("pledge"); + return 1; + } + + auto app = GUI::Application::construct(argc, argv); + app->event_loop().register_signal(SIGCHLD, [](int) { + // Wait all available children + while (waitpid(-1, nullptr, WNOHANG) > 0) + ; + }); + + if (pledge("stdio shared_buffer accept proc exec rpath", nullptr) < 0) { + perror("pledge"); + return 1; + } + + TaskbarWindow window; + window.show(); + + return app->exec(); +} |