CatDog: Help the user debug their programs
This adds helpful speech bubbles to CatDog. CatDog just wants to help, that's all.
This commit is contained in:
parent
da5923bc54
commit
e0fe38ea25
Notes:
sideshowbarker
2024-07-18 18:27:30 +09:00
Author: https://github.com/gunnarbeutner Commit: https://github.com/SerenityOS/serenity/commit/e0fe38ea25e Pull-request: https://github.com/SerenityOS/serenity/pull/6985
6 changed files with 132 additions and 0 deletions
Userland/Demos/CatDog
|
@ -1,5 +1,6 @@
|
|||
set(SOURCES
|
||||
CatDog.cpp
|
||||
SpeechBubble.cpp
|
||||
main.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());
|
||||
|
|
|
@ -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<void()> 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<Gfx::Bitmap> m_alert = *Gfx::Bitmap::load_from_file("/res/icons/catdog/alert.png");
|
||||
AK::NonnullRefPtr<Gfx::Bitmap> m_erun1 = *Gfx::Bitmap::load_from_file("/res/icons/catdog/erun1.png");
|
||||
|
|
40
Userland/Demos/CatDog/SpeechBubble.cpp
Normal file
40
Userland/Demos/CatDog/SpeechBubble.cpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Gunnar Beutner <gunnar@beutner.name>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "SpeechBubble.h"
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
|
||||
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();
|
||||
}
|
22
Userland/Demos/CatDog/SpeechBubble.h
Normal file
22
Userland/Demos/CatDog/SpeechBubble.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Gunnar Beutner <gunnar@beutner.name>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibGUI/Widget.h>
|
||||
|
||||
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<void()> on_dismiss;
|
||||
|
||||
private:
|
||||
SpeechBubble() = default;
|
||||
};
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
|
||||
#include "CatDog.h"
|
||||
#include "SpeechBubble.h"
|
||||
#include <LibCore/Timer.h>
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
|
@ -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<SpeechBubble>();
|
||||
advice_widget.set_layout<GUI::VerticalBoxLayout>();
|
||||
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();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue