mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 12:23:15 +00:00
CatDog: Simplify animation frame logic and fix minor bugs
Moved all images into a Vector instead of storing every animation frame in its own member variable. This greatly simplifies the bitmap selection logic and removes redundant if() checks. Also fixes minor state bugs. For example, CatDog woudld go to sleep immediately after actively moving for > 5 seconds. Also fixes arbitrary hardcoded values for mouse offset movement tresholds as well as inconsistent movement increments. This allows clicking anywhere on the CatDog window without moving CatDog. In addition to removing many member variables, the API interface is also cleaned up a bit to expose less CatDog internals. Nobody likes exposed CatDog internals ;). Variables and function are also renamed where necessary to (hopefully) improve readability.
This commit is contained in:
parent
213d82f39b
commit
e80f0b23e3
|
@ -12,172 +12,165 @@
|
||||||
|
|
||||||
ErrorOr<NonnullRefPtr<CatDog>> CatDog::create()
|
ErrorOr<NonnullRefPtr<CatDog>> CatDog::create()
|
||||||
{
|
{
|
||||||
|
struct ImageSource {
|
||||||
|
State state;
|
||||||
|
StringView path;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: The order of the elements is important. Matching is done in best-match order.
|
||||||
|
// So items with the more bits should be placed before items with less bits to
|
||||||
|
// ensure correct matching order. This also means that "Frame2" has to be first.
|
||||||
|
static constexpr Array<ImageSource, 24> const image_sources = {
|
||||||
|
ImageSource { State::Up | State::Right | State::Frame2, "/res/icons/catdog/nerun2.png"sv },
|
||||||
|
{ State::Up | State::Right, "/res/icons/catdog/nerun1.png"sv },
|
||||||
|
{ State::Up | State::Left | State::Frame2, "/res/icons/catdog/nwrun2.png"sv },
|
||||||
|
{ State::Up | State::Left, "/res/icons/catdog/nwrun1.png"sv },
|
||||||
|
{ State::Down | State::Right | State::Frame2, "/res/icons/catdog/serun2.png"sv },
|
||||||
|
{ State::Down | State::Right, "/res/icons/catdog/serun1.png"sv },
|
||||||
|
{ State::Down | State::Left | State::Frame2, "/res/icons/catdog/swrun2.png"sv },
|
||||||
|
{ State::Down | State::Left, "/res/icons/catdog/swrun1.png"sv },
|
||||||
|
{ State::Up | State::Frame2, "/res/icons/catdog/nrun2.png"sv },
|
||||||
|
{ State::Up, "/res/icons/catdog/nrun1.png"sv },
|
||||||
|
{ State::Down | State::Frame2, "/res/icons/catdog/srun2.png"sv },
|
||||||
|
{ State::Down, "/res/icons/catdog/srun1.png"sv },
|
||||||
|
{ State::Left | State::Frame2, "/res/icons/catdog/wrun2.png"sv },
|
||||||
|
{ State::Left, "/res/icons/catdog/wrun1.png"sv },
|
||||||
|
{ State::Right | State::Frame2, "/res/icons/catdog/erun2.png"sv },
|
||||||
|
{ State::Right, "/res/icons/catdog/erun1.png"sv },
|
||||||
|
{ State::Sleeping | State::Frame2, "/res/icons/catdog/sleep2.png"sv },
|
||||||
|
{ State::Sleeping, "/res/icons/catdog/sleep1.png"sv },
|
||||||
|
{ State::Idle | State::Artist, "/res/icons/catdog/artist.png"sv },
|
||||||
|
{ State::Idle | State::Inspector, "/res/icons/catdog/inspector.png"sv },
|
||||||
|
{ State::Idle, "/res/icons/catdog/still.png"sv },
|
||||||
|
{ State::Alert | State::Artist, "/res/icons/catdog/artist.png"sv },
|
||||||
|
{ State::Alert | State::Inspector, "/res/icons/catdog/inspector.png"sv },
|
||||||
|
{ State::Alert, "/res/icons/catdog/alert.png"sv }
|
||||||
|
};
|
||||||
|
|
||||||
auto catdog = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) CatDog));
|
auto catdog = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) CatDog));
|
||||||
catdog->m_alert = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/alert.png"sv));
|
for (auto const& image_source : image_sources)
|
||||||
catdog->m_artist = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/artist.png"sv));
|
TRY(catdog->m_images.try_append({ image_source.state, *TRY(Gfx::Bitmap::try_load_from_file(image_source.path)) }));
|
||||||
catdog->m_erun1 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/erun1.png"sv));
|
|
||||||
catdog->m_erun2 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/erun2.png"sv));
|
|
||||||
catdog->m_inspector = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/inspector.png"sv));
|
|
||||||
catdog->m_nerun1 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/nerun1.png"sv));
|
|
||||||
catdog->m_nerun2 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/nerun2.png"sv));
|
|
||||||
catdog->m_nrun1 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/nrun1.png"sv));
|
|
||||||
catdog->m_nrun2 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/nrun2.png"sv));
|
|
||||||
catdog->m_nwrun1 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/nwrun1.png"sv));
|
|
||||||
catdog->m_nwrun2 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/nwrun2.png"sv));
|
|
||||||
catdog->m_serun1 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/serun1.png"sv));
|
|
||||||
catdog->m_serun2 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/serun2.png"sv));
|
|
||||||
catdog->m_sleep1 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/sleep1.png"sv));
|
|
||||||
catdog->m_sleep2 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/sleep2.png"sv));
|
|
||||||
catdog->m_srun1 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/srun1.png"sv));
|
|
||||||
catdog->m_srun2 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/srun2.png"sv));
|
|
||||||
catdog->m_still = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/still.png"sv));
|
|
||||||
catdog->m_swrun1 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/swrun1.png"sv));
|
|
||||||
catdog->m_swrun2 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/swrun2.png"sv));
|
|
||||||
catdog->m_wrun1 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/wrun1.png"sv));
|
|
||||||
catdog->m_wrun2 = *TRY(Gfx::Bitmap::try_load_from_file("/res/icons/catdog/wrun2.png"sv));
|
|
||||||
catdog->m_curr_bmp = catdog->m_alert;
|
|
||||||
return catdog;
|
return catdog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CatDog::CatDog()
|
||||||
|
: m_proc_all(MUST(Core::Stream::File::open("/sys/kernel/processes"sv, Core::Stream::OpenMode::Read)))
|
||||||
|
{
|
||||||
|
m_idle_sleep_timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CatDog::set_roaming(bool roaming)
|
||||||
|
{
|
||||||
|
m_state = (roaming ? State::Idle : State::Alert) | special_application_states();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
CatDog::State CatDog::special_application_states() const
|
||||||
|
{
|
||||||
|
auto maybe_proc_info = Core::ProcessStatisticsReader::get_all(*m_proc_all);
|
||||||
|
if (maybe_proc_info.is_error())
|
||||||
|
return State::GenericCatDog;
|
||||||
|
|
||||||
|
auto proc_info = maybe_proc_info.release_value();
|
||||||
|
auto maybe_paint_program = proc_info.processes.first_matching([](auto& process) {
|
||||||
|
return process.name.equals_ignoring_case("pixelpaint"sv) || process.name.equals_ignoring_case("fonteditor"sv);
|
||||||
|
});
|
||||||
|
if (maybe_paint_program.has_value())
|
||||||
|
return State::Artist;
|
||||||
|
|
||||||
|
auto maybe_inspector_program = proc_info.processes.first_matching([](auto& process) {
|
||||||
|
return process.name.equals_ignoring_case("inspector"sv) || process.name.equals_ignoring_case("systemmonitor"sv) || process.name.equals_ignoring_case("profiler"sv);
|
||||||
|
});
|
||||||
|
if (maybe_inspector_program.has_value())
|
||||||
|
return State::Inspector;
|
||||||
|
|
||||||
|
return State::GenericCatDog;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CatDog::is_artist() const
|
||||||
|
{
|
||||||
|
return has_flag(special_application_states(), State::Artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CatDog::is_inspector() const
|
||||||
|
{
|
||||||
|
return has_flag(special_application_states(), State::Inspector);
|
||||||
|
}
|
||||||
|
|
||||||
void CatDog::timer_event(Core::TimerEvent&)
|
void CatDog::timer_event(Core::TimerEvent&)
|
||||||
{
|
{
|
||||||
auto maybe_proc_info = Core::ProcessStatisticsReader::get_all(*m_proc_all);
|
if (has_flag(m_state, State::Alert))
|
||||||
if (!maybe_proc_info.is_error()) {
|
|
||||||
auto proc_info = maybe_proc_info.release_value();
|
|
||||||
|
|
||||||
auto maybe_paint_program = proc_info.processes.first_matching([](auto& process) {
|
|
||||||
return process.name.equals_ignoring_case("pixelpaint"sv) || process.name.equals_ignoring_case("fonteditor"sv);
|
|
||||||
});
|
|
||||||
if (maybe_paint_program.has_value()) {
|
|
||||||
m_main_state = MainState::Artist;
|
|
||||||
} else {
|
|
||||||
auto maybe_inspector_program = proc_info.processes.first_matching([](auto& process) {
|
|
||||||
return process.name.equals_ignoring_case("inspector"sv) || process.name.equals_ignoring_case("systemmonitor"sv) || process.name.equals_ignoring_case("profiler"sv);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (maybe_inspector_program.has_value())
|
|
||||||
m_main_state = MainState::Inspector;
|
|
||||||
// If we currently have an application state but that app isn't open anymore, go back to idle.
|
|
||||||
else if (!is_non_application_state(m_main_state))
|
|
||||||
m_main_state = MainState::Idle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_roaming)
|
|
||||||
return;
|
return;
|
||||||
if (m_temp_pos.x() > 48) {
|
|
||||||
m_left = false;
|
|
||||||
m_right = true;
|
|
||||||
m_moveX = 16;
|
|
||||||
|
|
||||||
m_curr_bmp = m_erun1;
|
m_state = special_application_states();
|
||||||
if (m_curr_frame == 2)
|
|
||||||
m_curr_bmp = m_erun2;
|
|
||||||
} else if (m_temp_pos.x() < -16) {
|
|
||||||
m_left = true;
|
|
||||||
m_right = false;
|
|
||||||
m_moveX = -16;
|
|
||||||
|
|
||||||
m_curr_bmp = m_wrun1;
|
auto const size = window()->size();
|
||||||
if (m_curr_frame == 2)
|
Gfx::IntPoint move;
|
||||||
m_curr_bmp = m_wrun2;
|
|
||||||
|
if (m_mouse_offset.x() < 0) {
|
||||||
|
m_state |= State::Left;
|
||||||
|
move.set_x(max(m_mouse_offset.x(), -size.width() / 2));
|
||||||
|
} else if (m_mouse_offset.x() > size.width()) {
|
||||||
|
m_state |= State::Right;
|
||||||
|
move.set_x(min(m_mouse_offset.x(), size.width() / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_mouse_offset.y() < 0) {
|
||||||
|
m_state |= State::Up;
|
||||||
|
move.set_y(max(m_mouse_offset.y(), -size.height() / 2));
|
||||||
|
} else if (m_mouse_offset.y() > size.height()) {
|
||||||
|
m_state |= State::Down;
|
||||||
|
move.set_y(min(m_mouse_offset.y(), size.height() / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_any_flag(m_state, State::Directions)) {
|
||||||
|
m_idle_sleep_timer.start();
|
||||||
} else {
|
} else {
|
||||||
m_left = false;
|
if (m_idle_sleep_timer.elapsed() > 5'000)
|
||||||
m_right = false;
|
m_state |= State::Sleeping;
|
||||||
m_moveX = 0;
|
else
|
||||||
|
m_state |= State::Idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_temp_pos.y() > 48) {
|
window()->move_to(window()->position() + move);
|
||||||
m_up = false;
|
m_mouse_offset -= move;
|
||||||
m_down = true;
|
|
||||||
m_moveY = 10;
|
|
||||||
|
|
||||||
m_curr_bmp = m_srun1;
|
m_frame = m_frame == State::Frame1 ? State::Frame2 : State::Frame1;
|
||||||
if (m_curr_frame == 2)
|
m_state |= m_frame;
|
||||||
m_curr_bmp = m_srun2;
|
|
||||||
} else if (m_temp_pos.y() < -16) {
|
|
||||||
m_up = true;
|
|
||||||
m_down = false;
|
|
||||||
m_moveY = -10;
|
|
||||||
|
|
||||||
m_curr_bmp = m_nrun1;
|
|
||||||
if (m_curr_frame == 2)
|
|
||||||
m_curr_bmp = m_nrun2;
|
|
||||||
} else {
|
|
||||||
m_up = false;
|
|
||||||
m_down = false;
|
|
||||||
m_moveY = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_up && m_left) {
|
|
||||||
m_curr_bmp = m_nwrun1;
|
|
||||||
if (m_curr_frame == 2)
|
|
||||||
m_curr_bmp = m_nwrun2;
|
|
||||||
} else if (m_up && m_right) {
|
|
||||||
m_curr_bmp = m_nerun1;
|
|
||||||
if (m_curr_frame == 2)
|
|
||||||
m_curr_bmp = m_nerun2;
|
|
||||||
} else if (m_down && m_left) {
|
|
||||||
m_curr_bmp = m_swrun1;
|
|
||||||
if (m_curr_frame == 2)
|
|
||||||
m_curr_bmp = m_swrun2;
|
|
||||||
} else if (m_down && m_right) {
|
|
||||||
m_curr_bmp = m_serun1;
|
|
||||||
if (m_curr_frame == 2)
|
|
||||||
m_curr_bmp = m_serun2;
|
|
||||||
}
|
|
||||||
|
|
||||||
window()->move_to(window()->position().x() + m_moveX, window()->position().y() + m_moveY);
|
|
||||||
m_temp_pos.set_x(m_temp_pos.x() + (-m_moveX));
|
|
||||||
m_temp_pos.set_y(m_temp_pos.y() + (-m_moveY));
|
|
||||||
|
|
||||||
if (m_curr_frame == 1) {
|
|
||||||
m_curr_frame = 2;
|
|
||||||
} else {
|
|
||||||
m_curr_frame = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_up && !m_down && !m_left && !m_right) {
|
|
||||||
// Select the movement-free image based on the main state.
|
|
||||||
if (m_timer.elapsed() > 5000)
|
|
||||||
m_main_state = MainState::Sleeping;
|
|
||||||
set_image_by_main_state();
|
|
||||||
} else if (is_non_application_state(m_main_state)) {
|
|
||||||
// If CatDog currently moves, it should be idle the next time it stops.
|
|
||||||
m_main_state = MainState::Idle;
|
|
||||||
}
|
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Gfx::Bitmap& CatDog::bitmap_for_state() const
|
||||||
|
{
|
||||||
|
auto const iter = m_images.find_if([&](auto const& image) { return (m_state & image.state) == image.state; });
|
||||||
|
return iter != m_images.end() ? *iter->bitmap : *m_images[m_images.size() - 1].bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
void CatDog::paint_event(GUI::PaintEvent& event)
|
void CatDog::paint_event(GUI::PaintEvent& event)
|
||||||
{
|
{
|
||||||
|
auto const& bmp = bitmap_for_state();
|
||||||
GUI::Painter painter(*this);
|
GUI::Painter painter(*this);
|
||||||
painter.clear_rect(event.rect(), Gfx::Color());
|
painter.clear_rect(event.rect(), Gfx::Color());
|
||||||
painter.blit(Gfx::IntPoint(0, 0), *m_curr_bmp, m_curr_bmp->rect());
|
painter.blit(Gfx::IntPoint(0, 0), bmp, bmp.rect());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatDog::track_mouse_move(Gfx::IntPoint point)
|
void CatDog::track_mouse_move(Gfx::IntPoint point)
|
||||||
{
|
{
|
||||||
if (!m_roaming)
|
if (has_flag(m_state, State::Alert))
|
||||||
return;
|
return;
|
||||||
Gfx::IntPoint relative_point = point - window()->position();
|
|
||||||
if (m_temp_pos == relative_point)
|
Gfx::IntPoint relative_offset = point - window()->position();
|
||||||
return;
|
if (m_mouse_offset != relative_offset) {
|
||||||
m_temp_pos = relative_point;
|
m_mouse_offset = relative_offset;
|
||||||
m_timer.start();
|
m_idle_sleep_timer.start();
|
||||||
if (m_main_state == MainState::Sleeping) {
|
|
||||||
m_main_state = MainState::Alerted;
|
|
||||||
set_image_by_main_state();
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatDog::mousedown_event(GUI::MouseEvent& event)
|
void CatDog::mousedown_event(GUI::MouseEvent& event)
|
||||||
{
|
{
|
||||||
if (event.button() != GUI::MouseButton::Primary)
|
if (event.button() == GUI::MouseButton::Primary && on_click)
|
||||||
return;
|
|
||||||
if (on_click)
|
|
||||||
on_click();
|
on_click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/EnumBits.h>
|
||||||
#include <AK/NonnullRefPtr.h>
|
#include <AK/NonnullRefPtr.h>
|
||||||
#include <AK/RefPtr.h>
|
#include <AK/RefPtr.h>
|
||||||
|
#include <AK/Types.h>
|
||||||
#include <LibCore/ElapsedTimer.h>
|
#include <LibCore/ElapsedTimer.h>
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/File.h>
|
||||||
#include <LibCore/Stream.h>
|
#include <LibCore/Stream.h>
|
||||||
|
@ -25,112 +27,61 @@ class CatDog final : public GUI::Widget
|
||||||
public:
|
public:
|
||||||
static ErrorOr<NonnullRefPtr<CatDog>> create();
|
static ErrorOr<NonnullRefPtr<CatDog>> create();
|
||||||
|
|
||||||
// The general state, does not contain movement direction or whether CatDog is roaming.
|
|
||||||
enum class MainState {
|
|
||||||
Idle, // default state
|
|
||||||
Alerted, // woken by mouse cursor or speaking after being idle
|
|
||||||
Sleeping, // mouse hasn't moved in some time
|
|
||||||
Artist, // PixelPaint or FontEditor are open
|
|
||||||
Inspector, // SystemServer, Profiler or Inspector are open
|
|
||||||
};
|
|
||||||
static bool is_non_application_state(MainState state)
|
|
||||||
{
|
|
||||||
return state == MainState::Idle || state == MainState::Alerted || state == MainState::Sleeping;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void timer_event(Core::TimerEvent&) override;
|
virtual void timer_event(Core::TimerEvent&) override;
|
||||||
virtual void paint_event(GUI::PaintEvent& event) override;
|
virtual void paint_event(GUI::PaintEvent& event) override;
|
||||||
virtual void track_mouse_move(Gfx::IntPoint point) override;
|
virtual void track_mouse_move(Gfx::IntPoint point) override;
|
||||||
virtual void mousedown_event(GUI::MouseEvent& event) override;
|
virtual void mousedown_event(GUI::MouseEvent& event) override;
|
||||||
virtual void context_menu_event(GUI::ContextMenuEvent& event) override;
|
virtual void context_menu_event(GUI::ContextMenuEvent& event) override;
|
||||||
|
|
||||||
void start_the_timer() { m_timer.start(); }
|
|
||||||
|
|
||||||
Function<void()> on_click;
|
Function<void()> on_click;
|
||||||
Function<void(GUI::ContextMenuEvent&)> on_context_menu_request;
|
Function<void(GUI::ContextMenuEvent&)> on_context_menu_request;
|
||||||
|
|
||||||
bool roaming() const { return m_roaming; }
|
void set_roaming(bool roaming);
|
||||||
void set_roaming(bool roaming)
|
|
||||||
{
|
|
||||||
m_roaming = roaming;
|
|
||||||
if (!roaming) {
|
|
||||||
// If we stop CatDog while it's in a program-specific state, we don't want it to be alerted.
|
|
||||||
if (m_main_state == MainState::Idle || m_main_state == MainState::Sleeping)
|
|
||||||
m_main_state = MainState::Alerted;
|
|
||||||
m_curr_frame = 0;
|
|
||||||
set_image_by_main_state();
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MainState main_state() const { return m_main_state; }
|
[[nodiscard]] bool is_artist() const;
|
||||||
|
[[nodiscard]] bool is_inspector() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Gfx::IntPoint m_temp_pos;
|
enum class State : u16 {
|
||||||
Core::ElapsedTimer m_timer;
|
Frame1 = 0x0,
|
||||||
|
Frame2 = 0x1,
|
||||||
|
|
||||||
int m_curr_frame = 1;
|
Up = 0x10,
|
||||||
int m_moveX, m_moveY = 0;
|
Down = 0x20,
|
||||||
MainState m_main_state { MainState::Alerted };
|
Left = 0x40,
|
||||||
bool m_up, m_down, m_left, m_right;
|
Right = 0x80,
|
||||||
bool m_roaming { true };
|
|
||||||
|
Directions = Up | Down | Left | Right,
|
||||||
|
|
||||||
|
Roaming = 0x0100,
|
||||||
|
Idle = 0x0200,
|
||||||
|
Sleeping = 0x0400,
|
||||||
|
Alert = 0x0800,
|
||||||
|
|
||||||
|
GenericCatDog = 0x0000,
|
||||||
|
Inspector = 0x1000,
|
||||||
|
Artist = 0x2000
|
||||||
|
};
|
||||||
|
|
||||||
|
AK_ENUM_BITWISE_FRIEND_OPERATORS(State);
|
||||||
|
|
||||||
|
struct ImageForState {
|
||||||
|
State state;
|
||||||
|
NonnullRefPtr<Gfx::Bitmap> bitmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<ImageForState> m_images;
|
||||||
|
|
||||||
|
Gfx::IntPoint m_mouse_offset {};
|
||||||
|
Core::ElapsedTimer m_idle_sleep_timer;
|
||||||
|
|
||||||
NonnullOwnPtr<Core::Stream::File> m_proc_all;
|
NonnullOwnPtr<Core::Stream::File> m_proc_all;
|
||||||
|
|
||||||
RefPtr<Gfx::Bitmap> m_alert;
|
State m_state { State::Roaming };
|
||||||
RefPtr<Gfx::Bitmap> m_artist;
|
State m_frame { State::Frame1 };
|
||||||
RefPtr<Gfx::Bitmap> m_erun1;
|
|
||||||
RefPtr<Gfx::Bitmap> m_erun2;
|
|
||||||
RefPtr<Gfx::Bitmap> m_inspector;
|
|
||||||
RefPtr<Gfx::Bitmap> m_nerun1;
|
|
||||||
RefPtr<Gfx::Bitmap> m_nerun2;
|
|
||||||
RefPtr<Gfx::Bitmap> m_nrun1;
|
|
||||||
RefPtr<Gfx::Bitmap> m_nrun2;
|
|
||||||
RefPtr<Gfx::Bitmap> m_nwrun1;
|
|
||||||
RefPtr<Gfx::Bitmap> m_nwrun2;
|
|
||||||
RefPtr<Gfx::Bitmap> m_serun1;
|
|
||||||
RefPtr<Gfx::Bitmap> m_serun2;
|
|
||||||
RefPtr<Gfx::Bitmap> m_sleep1;
|
|
||||||
RefPtr<Gfx::Bitmap> m_sleep2;
|
|
||||||
RefPtr<Gfx::Bitmap> m_srun1;
|
|
||||||
RefPtr<Gfx::Bitmap> m_srun2;
|
|
||||||
RefPtr<Gfx::Bitmap> m_still;
|
|
||||||
RefPtr<Gfx::Bitmap> m_swrun1;
|
|
||||||
RefPtr<Gfx::Bitmap> m_swrun2;
|
|
||||||
RefPtr<Gfx::Bitmap> m_wrun1;
|
|
||||||
RefPtr<Gfx::Bitmap> m_wrun2;
|
|
||||||
|
|
||||||
RefPtr<Gfx::Bitmap> m_curr_bmp;
|
CatDog();
|
||||||
|
|
||||||
// Used if CatDog is still; may also account for animation frames.
|
[[nodiscard]] Gfx::Bitmap& bitmap_for_state() const;
|
||||||
void set_image_by_main_state()
|
[[nodiscard]] State special_application_states() const;
|
||||||
{
|
|
||||||
switch (m_main_state) {
|
|
||||||
case MainState::Idle:
|
|
||||||
m_curr_bmp = m_still;
|
|
||||||
break;
|
|
||||||
case MainState::Alerted:
|
|
||||||
m_curr_bmp = m_alert;
|
|
||||||
break;
|
|
||||||
case MainState::Sleeping:
|
|
||||||
if (m_curr_frame == 1)
|
|
||||||
m_curr_bmp = m_sleep1;
|
|
||||||
else
|
|
||||||
m_curr_bmp = m_sleep2;
|
|
||||||
break;
|
|
||||||
case MainState::Artist:
|
|
||||||
m_curr_bmp = m_artist;
|
|
||||||
break;
|
|
||||||
case MainState::Inspector:
|
|
||||||
m_curr_bmp = m_inspector;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CatDog()
|
|
||||||
: m_temp_pos { 0, 0 }
|
|
||||||
, m_proc_all(MUST(Core::Stream::File::open("/sys/kernel/processes"sv, Core::Stream::OpenMode::Read)))
|
|
||||||
{
|
|
||||||
set_image_by_main_state();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,8 @@ static Array<StringView, 3> default_messages = {
|
||||||
"It looks like you're trying to shave\na yak. Would you like some help?"sv,
|
"It looks like you're trying to shave\na yak. Would you like some help?"sv,
|
||||||
"Well Hello Friend!"sv,
|
"Well Hello Friend!"sv,
|
||||||
};
|
};
|
||||||
static Array<StringView, 3> pixel_paint_messages = {
|
|
||||||
|
static Array<StringView, 3> artist_messages = {
|
||||||
"It looks like you're creating art.\nWould you like some help?"sv,
|
"It looks like you're creating art.\nWould you like some help?"sv,
|
||||||
"It looks like you're making a meme\nfor Discord. \U0010CD65"sv,
|
"It looks like you're making a meme\nfor Discord. \U0010CD65"sv,
|
||||||
"It looks like you're using the filter\ngallery. Would you like a suggestion?"sv,
|
"It looks like you're using the filter\ngallery. Would you like a suggestion?"sv,
|
||||||
|
@ -48,7 +49,7 @@ void SpeechBubble::paint_event(GUI::PaintEvent&)
|
||||||
painter.draw_line(connector_top_left, Gfx::IntPoint { connector_bottom.x() - 1, connector_bottom.y() }, palette().active_window_border1());
|
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_line(connector_top_right, connector_bottom, palette().active_window_border1());
|
||||||
|
|
||||||
auto& message_list = m_cat_dog->main_state() == CatDog::MainState::Artist ? pixel_paint_messages : (m_cat_dog->main_state() == CatDog::MainState::Inspector ? inspector_messages : default_messages);
|
auto& message_list = m_cat_dog->is_artist() ? artist_messages : (m_cat_dog->is_inspector() ? inspector_messages : default_messages);
|
||||||
auto message = message_list[get_random<u8>() % message_list.size()];
|
auto message = message_list[get_random<u8>() % message_list.size()];
|
||||||
painter.draw_text(text_area, message, Gfx::TextAlignment::Center);
|
painter.draw_text(text_area, message, Gfx::TextAlignment::Center);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,6 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
window->show();
|
window->show();
|
||||||
window->set_always_on_top();
|
window->set_always_on_top();
|
||||||
catdog_widget->start_timer(250, Core::TimerShouldFireWhenNotVisible::Yes);
|
catdog_widget->start_timer(250, Core::TimerShouldFireWhenNotVisible::Yes);
|
||||||
catdog_widget->start_the_timer(); // timer for "mouse sleep detection"
|
|
||||||
|
|
||||||
auto advice_window = TRY(GUI::Window::try_create());
|
auto advice_window = TRY(GUI::Window::try_create());
|
||||||
advice_window->set_title("CatDog Advice");
|
advice_window->set_title("CatDog Advice");
|
||||||
|
@ -68,7 +67,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
advice_widget->layout()->set_spacing(0);
|
advice_widget->layout()->set_spacing(0);
|
||||||
|
|
||||||
auto advice_timer = TRY(Core::Timer::try_create());
|
auto advice_timer = TRY(Core::Timer::try_create());
|
||||||
advice_timer->set_interval(15000);
|
advice_timer->set_interval(15'000);
|
||||||
advice_timer->set_single_shot(true);
|
advice_timer->set_single_shot(true);
|
||||||
advice_timer->on_timeout = [&] {
|
advice_timer->on_timeout = [&] {
|
||||||
window->move_to_front();
|
window->move_to_front();
|
||||||
|
|
Loading…
Reference in a new issue