diff --git a/Base/res/apps/AnalogClock.af b/Base/res/apps/AnalogClock.af new file mode 100644 index 0000000000..28fec009ff --- /dev/null +++ b/Base/res/apps/AnalogClock.af @@ -0,0 +1,4 @@ +[App] +Name=Analog Clock +Executable=/bin/AnalogClock +Category=Utilities diff --git a/Base/res/icons/16x16/app-analog-clock.png b/Base/res/icons/16x16/app-analog-clock.png new file mode 100644 index 0000000000..e57522add3 Binary files /dev/null and b/Base/res/icons/16x16/app-analog-clock.png differ diff --git a/Base/res/icons/32x32/app-analog-clock.png b/Base/res/icons/32x32/app-analog-clock.png new file mode 100644 index 0000000000..5432bf4fd4 Binary files /dev/null and b/Base/res/icons/32x32/app-analog-clock.png differ diff --git a/Userland/Applications/AnalogClock/AnalogClock.cpp b/Userland/Applications/AnalogClock/AnalogClock.cpp new file mode 100644 index 0000000000..8115d7b60c --- /dev/null +++ b/Userland/Applications/AnalogClock/AnalogClock.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021, Erlend Høier + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "AnalogClock.h" +#include +#include +#include +#include +#include + +void AnalogClock::draw_graduations(GUI::Painter& painter, Gfx::IntRect& rect, int x, int y) +{ + rect.set_x(x); + rect.set_y(y); + + painter.fill_rect(rect, palette().active_window_border2()); + + painter.draw_line(rect.top_left(), rect.top_right(), palette().threed_highlight()); + painter.draw_line(rect.bottom_left(), rect.bottom_right(), palette().active_window_border1().darkened(0.7f)); + painter.draw_line(rect.bottom_right(), rect.top_right(), palette().active_window_border1().darkened(0.7f)); + painter.draw_line(rect.top_left(), rect.bottom_left(), palette().threed_highlight()); +} + +// To create an even clock face it's necessary to mirror the graduations positions +void AnalogClock::draw_mirrored_graduations(GUI::Painter& painter, Gfx::IntRect& rect, int x, int y, int rect_center_offset) +{ + auto w = this->rect().center().x() - rect_center_offset; + auto h = this->rect().center().y() - rect_center_offset; + + draw_graduations(painter, rect, x + w, y + h); + draw_graduations(painter, rect, y + w, x + h); + draw_graduations(painter, rect, -x + w, y + h); + draw_graduations(painter, rect, -y + w, x + h); + draw_graduations(painter, rect, x + w, -y + h); + draw_graduations(painter, rect, y + w, -x + h); + draw_graduations(painter, rect, -x + w, -y + h); + draw_graduations(painter, rect, -y + w, -x + h); +} + +void AnalogClock::draw_face(GUI::Painter& painter) +{ + int x, y; + double angle = 2 * M_PI / 60; + + for (int i = 0; i <= 7; ++i) { + x = sin(angle * static_cast(i)) * m_clock_face_radius; + y = cos(angle * static_cast(i)) * m_clock_face_radius; + + draw_mirrored_graduations(painter, m_small_graduation_square, x, y, 1); + + if (i % 5 == 0) + draw_mirrored_graduations(painter, m_big_graduation_square, x, y, 2); + } +} + +void AnalogClock::draw_hand(GUI::Painter& painter, double angle, double length, Gfx::Color hand_color) +{ + if (angle >= 2 * M_PI) + angle -= 2 * M_PI; + + double cosine = cos(angle); + double sine = sin(angle); + + double hand_x = (rect().center().x() + (cosine * length)); + double hand_y = (rect().center().y() + (sine * length)); + + Gfx::IntPoint indicator_point(hand_x, hand_y); + Gfx::IntPoint tail_point(rect().center().x() + (-cosine * m_hand_tail_length), rect().center().y() + (-sine * m_hand_tail_length)); + Gfx::IntPoint right_wing_point(rect().center().x() + (-sine * m_hand_wing_span), rect().center().y() + (cosine * m_hand_wing_span)); + Gfx::IntPoint left_wing_point(rect().center().x() + (sine * m_hand_wing_span), rect().center().y() + (-cosine * m_hand_wing_span)); + + Gfx::Path hand_fill; + hand_fill.move_to(static_cast(indicator_point)); + hand_fill.line_to(static_cast(left_wing_point)); + hand_fill.line_to(static_cast(tail_point)); + hand_fill.line_to(static_cast(right_wing_point)); + hand_fill.close(); + + painter.fill_path(hand_fill, hand_color, Gfx::Painter::WindingRule::Nonzero); + + // Draw highlight depending on the angle, this creates a subtle 3d effect. + // remember the angle value is offset by half pi. + if (angle > M_PI_2 - (M_PI / 3) && angle < M_PI + (M_PI / 3)) { + painter.draw_line(left_wing_point, indicator_point, hand_color.darkened(0.7f)); + painter.draw_line(left_wing_point, tail_point, hand_color.darkened(0.7f)); + + painter.draw_line(right_wing_point, indicator_point, palette().threed_highlight()); + painter.draw_line(right_wing_point, tail_point, palette().threed_highlight()); + } else { + painter.draw_line(right_wing_point, indicator_point, hand_color.darkened(0.7f)); + painter.draw_line(right_wing_point, tail_point, hand_color.darkened(0.7f)); + + painter.draw_line(left_wing_point, indicator_point, palette().threed_highlight()); + painter.draw_line(left_wing_point, tail_point, palette().threed_highlight()); + } +}; + +void AnalogClock::draw_seconds_hand(GUI::Painter& painter, double angle) +{ + double hand_x = (rect().center().x() + (cos(angle)) * (m_clock_face_radius - 10)); + double hand_y = (rect().center().y() + (sin(angle)) * (m_clock_face_radius - 10)); + + Gfx::IntPoint indicator_point(hand_x, hand_y); + painter.draw_line(rect().center(), indicator_point, palette().base_text()); +} + +void AnalogClock::paint_event(GUI::PaintEvent& event) +{ + GUI::Painter painter(*this); + painter.clear_rect(event.rect(), palette().window()); + + draw_face(painter); + + auto time = Core::DateTime::now(); + auto minute = time.minute() * (2 * M_PI) / 60; + auto hour = (minute + (2 * M_PI) * time.hour()) / 12; + auto seconds = time.second() * (2 * M_PI) / 60; + auto angle_offset = M_PI_2; + + draw_hand(painter, minute - angle_offset, m_minute_hand_length, palette().active_window_border2()); + draw_hand(painter, hour - angle_offset, m_hour_hand_length, palette().active_window_border1()); + draw_seconds_hand(painter, seconds - angle_offset); + + if (time.hour() == 0) + update_title_date(); +} + +void AnalogClock::update_title_date() +{ + window()->set_title(Core::DateTime::now().to_string("Clock %d-%m-%Y")); +} diff --git a/Userland/Applications/AnalogClock/AnalogClock.h b/Userland/Applications/AnalogClock/AnalogClock.h new file mode 100644 index 0000000000..6735ad2e29 --- /dev/null +++ b/Userland/Applications/AnalogClock/AnalogClock.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Erlend Høier + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +class AnalogClock : public GUI::Widget { + C_OBJECT(AnalogClock) +public: + ~AnalogClock() override = default; + +private: + AnalogClock() + : m_small_graduation_square(Gfx::IntRect({}, { 3, 3 })) + , m_big_graduation_square(Gfx::IntRect({}, { 5, 5 })) + { + start_timer(1000); + } + + unsigned m_clock_face_radius { 70 }; + Gfx::IntRect m_small_graduation_square; + Gfx::IntRect m_big_graduation_square; + + unsigned m_minute_hand_length { 58 }; + unsigned m_hour_hand_length { 42 }; + + double m_hand_tail_length { 22 }; + double m_hand_wing_span { 5 }; + +protected: + void paint_event(GUI::PaintEvent&) override; + void draw_face(GUI::Painter&); + void draw_mirrored_graduations(GUI::Painter&, Gfx::IntRect&, int x, int y, int rect_center_offset); + void draw_graduations(GUI::Painter&, Gfx::IntRect&, int x, int y); + void draw_hand(GUI::Painter&, double angle, double length, Gfx::Color hand_color); + void draw_seconds_hand(GUI::Painter&, double angle); + void update_title_date(); + + void timer_event(Core::TimerEvent&) override + { + update(); + } +}; diff --git a/Userland/Applications/AnalogClock/CMakeLists.txt b/Userland/Applications/AnalogClock/CMakeLists.txt new file mode 100644 index 0000000000..1597a97484 --- /dev/null +++ b/Userland/Applications/AnalogClock/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCES + main.cpp + AnalogClock.cpp + ) + +serenity_app(AnalogClock ICON app-analog-clock) +target_link_libraries(AnalogClock LibGUI) diff --git a/Userland/Applications/AnalogClock/main.cpp b/Userland/Applications/AnalogClock/main.cpp new file mode 100644 index 0000000000..fe54d596d1 --- /dev/null +++ b/Userland/Applications/AnalogClock/main.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021, Erlend Høier + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "AnalogClock.h" +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (pledge("stdio recvfd sendfd accept rpath unix cpath wpath fattr", nullptr) < 0) { + perror("pledge"); + return 1; + } + + auto app = GUI::Application::construct(argc, argv); + + if (pledge("stdio recvfd sendfd accept rpath cpath wpath", nullptr) < 0) { + perror("pledge"); + return 1; + } + + if (unveil("/res", "r") < 0) { + perror("unveil"); + return 1; + } + + if (unveil(nullptr, nullptr) < 0) { + perror("unveil"); + return 1; + } + + auto app_icon = GUI::Icon::default_icon("app-analog-clock"); + auto window = GUI::Window::construct(); + + window->set_main_widget(); + window->set_title(Core::DateTime::now().to_string("Clock %d-%m-%Y")); + window->set_icon(app_icon.bitmap_for_size(16)); + window->resize(170, 170); + window->set_resizable(false); + + window->show(); + return app->exec(); +} diff --git a/Userland/Applications/CMakeLists.txt b/Userland/Applications/CMakeLists.txt index c89932e292..c1ec252db0 100644 --- a/Userland/Applications/CMakeLists.txt +++ b/Userland/Applications/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(About) +add_subdirectory(AnalogClock) add_subdirectory(Browser) add_subdirectory(Calculator) add_subdirectory(Calendar)