diff --git a/Userland/Demos/CatDog/CMakeLists.txt b/Userland/Demos/CatDog/CMakeLists.txt index 3224787079..ca3e8f6e92 100644 --- a/Userland/Demos/CatDog/CMakeLists.txt +++ b/Userland/Demos/CatDog/CMakeLists.txt @@ -1,5 +1,6 @@ set(SOURCES CatDog.cpp + SpeechBubble.cpp main.cpp ) diff --git a/Userland/Demos/CatDog/CatDog.cpp b/Userland/Demos/CatDog/CatDog.cpp index 9f71cdb657..0d115f6da2 100644 --- a/Userland/Demos/CatDog/CatDog.cpp +++ b/Userland/Demos/CatDog/CatDog.cpp @@ -11,6 +11,8 @@ void CatDog::timer_event(Core::TimerEvent&) { + if (!m_roaming) + return; if (m_temp_pos.x() > 48) { m_left = false; m_right = true; @@ -105,6 +107,8 @@ void CatDog::paint_event(GUI::PaintEvent& event) void CatDog::mousemove_event(GUI::MouseEvent& event) { + if (!m_roaming) + return; if (m_temp_pos == event.position()) return; m_temp_pos = event.position(); @@ -115,6 +119,15 @@ void CatDog::mousemove_event(GUI::MouseEvent& event) } m_sleeping = false; } + +void CatDog::mousedown_event(GUI::MouseEvent& event) +{ + if (event.button() != GUI::MouseButton::Left) + return; + if (on_click) + on_click(); +} + void CatDog::track_cursor_globally() { VERIFY(window()); diff --git a/Userland/Demos/CatDog/CatDog.h b/Userland/Demos/CatDog/CatDog.h index 8fdeb96e52..630468920c 100644 --- a/Userland/Demos/CatDog/CatDog.h +++ b/Userland/Demos/CatDog/CatDog.h @@ -18,16 +18,32 @@ public: virtual void timer_event(Core::TimerEvent&) override; virtual void paint_event(GUI::PaintEvent& event) override; virtual void mousemove_event(GUI::MouseEvent& event) override; + virtual void mousedown_event(GUI::MouseEvent& event) override; void track_cursor_globally(); void start_the_timer() { m_timer.start(); } + Function on_click; + + bool roaming() const { return m_roaming; } + void set_roaming(bool roaming) + { + m_roaming = roaming; + if (!roaming) { + m_sleeping = false; + m_curr_frame = 0; + m_curr_bmp = m_alert; + update(); + } + } + private: Gfx::IntPoint m_temp_pos; Core::ElapsedTimer m_timer; int m_curr_frame = 1; int m_moveX, m_moveY = 0; bool m_up, m_down, m_left, m_right, m_sleeping = false; + bool m_roaming { true }; AK::NonnullRefPtr m_alert = *Gfx::Bitmap::load_from_file("/res/icons/catdog/alert.png"); AK::NonnullRefPtr m_erun1 = *Gfx::Bitmap::load_from_file("/res/icons/catdog/erun1.png"); diff --git a/Userland/Demos/CatDog/SpeechBubble.cpp b/Userland/Demos/CatDog/SpeechBubble.cpp new file mode 100644 index 0000000000..4ce092d22e --- /dev/null +++ b/Userland/Demos/CatDog/SpeechBubble.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021, Gunnar Beutner + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "SpeechBubble.h" +#include +#include + +void SpeechBubble::paint_event(GUI::PaintEvent&) +{ + GUI::Painter painter(*this); + painter.clear_rect(rect(), Gfx::Color()); + + constexpr auto background_color = Gfx::Color::from_rgb(0xeaf688); + + auto text_area = rect(); + text_area.set_height(text_area.height() - 10); + painter.draw_rect(text_area, palette().active_window_border1()); + text_area.shrink(2, 2); + painter.fill_rect(text_area, background_color); + + auto connector_top_left = Gfx::IntPoint { rect().width() / 2 - 5, text_area.height() + 1 }; + auto connector_top_right = Gfx::IntPoint { rect().width() / 2 + 5, text_area.height() + 1 }; + auto connector_bottom = Gfx::IntPoint { rect().width() / 2 + 10, rect().height() }; + painter.draw_triangle(connector_top_left, connector_top_right, connector_bottom, background_color); + painter.draw_line(connector_top_left, Gfx::IntPoint { connector_bottom.x() - 1, connector_bottom.y() }, palette().active_window_border1()); + painter.draw_line(connector_top_right, connector_bottom, palette().active_window_border1()); + + painter.draw_text(text_area, "It looks like you're trying to debug\na program. Would you like some help?", Gfx::TextAlignment::Center); +} + +void SpeechBubble::mousedown_event(GUI::MouseEvent& event) +{ + if (event.button() != GUI::MouseButton::Left) + return; + if (on_dismiss) + on_dismiss(); +} diff --git a/Userland/Demos/CatDog/SpeechBubble.h b/Userland/Demos/CatDog/SpeechBubble.h new file mode 100644 index 0000000000..9ee902f9cb --- /dev/null +++ b/Userland/Demos/CatDog/SpeechBubble.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021, Gunnar Beutner + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +class SpeechBubble final : public GUI::Widget { + C_OBJECT(SpeechBubble); + +public: + virtual void paint_event(GUI::PaintEvent&) override; + virtual void mousedown_event(GUI::MouseEvent&) override; + + Function on_dismiss; + +private: + SpeechBubble() = default; +}; diff --git a/Userland/Demos/CatDog/main.cpp b/Userland/Demos/CatDog/main.cpp index 89bd0dacce..302a285eba 100644 --- a/Userland/Demos/CatDog/main.cpp +++ b/Userland/Demos/CatDog/main.cpp @@ -5,6 +5,8 @@ */ #include "CatDog.h" +#include "SpeechBubble.h" +#include #include #include #include @@ -63,5 +65,43 @@ int main(int argc, char** argv) catdog_widget.start_timer(250, Core::TimerShouldFireWhenNotVisible::Yes); catdog_widget.start_the_timer(); // timer for "mouse sleep detection" + auto advice_window = GUI::Window::construct(); + advice_window->set_title("CatDog Advice"); + advice_window->resize(225, 50); + advice_window->set_frameless(true); + advice_window->set_resizable(false); + advice_window->set_has_alpha_channel(true); + advice_window->set_alpha_hit_threshold(1.0f); + + auto& advice_widget = advice_window->set_main_widget(); + advice_widget.set_layout(); + advice_widget.layout()->set_spacing(0); + + auto advice_timer = Core::Timer::construct(); + advice_timer->set_interval(15000); + advice_timer->set_single_shot(true); + advice_timer->on_timeout = [&] { + window->move_to_front(); + advice_window->move_to_front(); + catdog_widget.set_roaming(false); + advice_window->move_to(window->x() - advice_window->width() / 2, window->y() - advice_window->height()); + advice_window->show(); + }; + advice_timer->start(); + + advice_widget.on_dismiss = [&] { + catdog_widget.set_roaming(true); + advice_window->hide(); + advice_timer->start(); + }; + + // Let users toggle the advice functionality by clicking on catdog. + catdog_widget.on_click = [&] { + if (advice_timer->is_active()) + advice_timer->stop(); + else + advice_timer->start(); + }; + return app->exec(); }