From 4c43fc05151b48ae13261a18857c1351f0a56915 Mon Sep 17 00:00:00 2001 From: Erlend Date: Sun, 9 May 2021 15:51:36 +0200 Subject: [PATCH] AnalogClock: New analog clock application (#6760) --- Base/res/apps/AnalogClock.af | 4 + Base/res/icons/16x16/app-analog-clock.png | Bin 0 -> 357 bytes Base/res/icons/32x32/app-analog-clock.png | Bin 0 -> 997 bytes .../Applications/AnalogClock/AnalogClock.cpp | 134 ++++++++++++++++++ .../Applications/AnalogClock/AnalogClock.h | 48 +++++++ .../Applications/AnalogClock/CMakeLists.txt | 7 + Userland/Applications/AnalogClock/main.cpp | 49 +++++++ Userland/Applications/CMakeLists.txt | 1 + 8 files changed, 243 insertions(+) create mode 100644 Base/res/apps/AnalogClock.af create mode 100644 Base/res/icons/16x16/app-analog-clock.png create mode 100644 Base/res/icons/32x32/app-analog-clock.png create mode 100644 Userland/Applications/AnalogClock/AnalogClock.cpp create mode 100644 Userland/Applications/AnalogClock/AnalogClock.h create mode 100644 Userland/Applications/AnalogClock/CMakeLists.txt create mode 100644 Userland/Applications/AnalogClock/main.cpp 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 0000000000000000000000000000000000000000..e57522add34d20541b0b28b47c56b9456b101a2e GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s77>k44ofy`glX=O&z$owO z;uvDlyLOUp&LIbhw)tkOi3LhW)OLLro21!M86D=4|Er-khiB zsVCC<$Ec{J{`9iQ=O4d3`EAL~@WW>RiMgCzDdLy>=4+hjQPFag{JH6FnmP-^){g(fXE!rpgar7RHE{ zIR)gKR7C4tz4`3btgBVt*>)dmEb>g=e$j38lK5mZH>fxH%gk#vN%xo>L{j#5oyeZ@ zmzQy-lep*Pmfdmu4c})^RPrw0$}D@--q(HN8<7jGUskw2+W(N@aonU?Ev@$p7#J8B NJYD@<);T3K0RW^TnqdF{ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5432bf4fd451448ab016a11b14cd082eefe2d0c0 GIT binary patch literal 997 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}jKx9jP7LeL$-HD>V4m;k z;usRq`gW?XM@+cL@%s07i|2F7x?GGnzOBi#Tk}T1t<+5$TnZ8u7jnd=iso$g5KrV% zozK%a$;?Dyij(4wmwH!x7tR)B72n0pda^-9jAwF!iGpM)N2c2howR#j_nn^^xjeo2 z`QG0j|Ihuu^F7a=w-2}MPBfpt`sVrbVW%5^KCCv*oqO|BQv8pblZ`?ec)l7cMzLn@ zI>E7i8o$fzj_u5otai>me^p5`J)yWrsNwokqto5pHHF`D{{DOy&+{rPTA)*S_s^KK zSIn80ic96c;$gV``t=tcUe;^-?_WIl?4RAbppd%X#tf7G?&v=EXKuvW8`JmgF#jX> zUu^zzuJb*?k|> zr5QKq&-Hzm{?3Z;lE4SoTJ{=+`g68LN7i^2E534gysSDn-RD`~V+F67%tudMVegoI zS?SOAjR%*P*Ljp2WVo?afA0$6?{c#n<1y3j(87cS^5*7t zyYr4$NW<~)tjUWPX9&hD4On-2ZT9sY`!_j7bzEB}xzlgUeqDV%ryl0Cr-#?`>m7UT z9lWA#hqToFk2afshA#E9F|#{Yv(Ta3Y&KWquXjdESEW7w7_9dH=k3N8wIvXS%e&rr#%De{OB<8F8+j-hOqie39++x?stTVr>AE_$wRjz{}>wnx*M)ooguht_UyomGq~9&xffB*RBtf6uk&smoLl>BBc?ePCc< OVDNPHb6Mw<&;$Tstm3Tz literal 0 HcmV?d00001 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)