AnalogClock: New analog clock application (#6760)
This commit is contained in:
parent
c53e937014
commit
4c43fc0515
Notes:
sideshowbarker
2024-07-18 18:26:38 +09:00
Author: https://github.com/ReasonablePanic 🔰 Commit: https://github.com/SerenityOS/serenity/commit/4c43fc05151 Pull-request: https://github.com/SerenityOS/serenity/pull/6760 Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/bcoles Reviewed-by: https://github.com/pakerwreah
8 changed files with 243 additions and 0 deletions
4
Base/res/apps/AnalogClock.af
Normal file
4
Base/res/apps/AnalogClock.af
Normal file
|
@ -0,0 +1,4 @@
|
|||
[App]
|
||||
Name=Analog Clock
|
||||
Executable=/bin/AnalogClock
|
||||
Category=Utilities
|
BIN
Base/res/icons/16x16/app-analog-clock.png
Normal file
BIN
Base/res/icons/16x16/app-analog-clock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 357 B |
BIN
Base/res/icons/32x32/app-analog-clock.png
Normal file
BIN
Base/res/icons/32x32/app-analog-clock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 997 B |
134
Userland/Applications/AnalogClock/AnalogClock.cpp
Normal file
134
Userland/Applications/AnalogClock/AnalogClock.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Erlend Høier <Erlend@ReasonablePanic.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "AnalogClock.h"
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibGfx/Path.h>
|
||||
|
||||
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<double>(i)) * m_clock_face_radius;
|
||||
y = cos(angle * static_cast<double>(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<Gfx::FloatPoint>(indicator_point));
|
||||
hand_fill.line_to(static_cast<Gfx::FloatPoint>(left_wing_point));
|
||||
hand_fill.line_to(static_cast<Gfx::FloatPoint>(tail_point));
|
||||
hand_fill.line_to(static_cast<Gfx::FloatPoint>(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"));
|
||||
}
|
48
Userland/Applications/AnalogClock/AnalogClock.h
Normal file
48
Userland/Applications/AnalogClock/AnalogClock.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Erlend Høier <Erlend@ReasonablePanic.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibGUI/Painter.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
7
Userland/Applications/AnalogClock/CMakeLists.txt
Normal file
7
Userland/Applications/AnalogClock/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
AnalogClock.cpp
|
||||
)
|
||||
|
||||
serenity_app(AnalogClock ICON app-analog-clock)
|
||||
target_link_libraries(AnalogClock LibGUI)
|
49
Userland/Applications/AnalogClock/main.cpp
Normal file
49
Userland/Applications/AnalogClock/main.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Erlend Høier <Erlend@ReasonablePanic.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "AnalogClock.h"
|
||||
#include <LibCore/DateTime.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/Icon.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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<AnalogClock>();
|
||||
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();
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
add_subdirectory(About)
|
||||
add_subdirectory(AnalogClock)
|
||||
add_subdirectory(Browser)
|
||||
add_subdirectory(Calculator)
|
||||
add_subdirectory(Calendar)
|
||||
|
|
Loading…
Add table
Reference in a new issue