mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 08:17:35 +00:00
ClockSettings+Taskbar: Add settings for taskbar clock format
This commit is contained in:
parent
36b6356ce5
commit
5bb0b6ba7a
13 changed files with 334 additions and 179 deletions
|
@ -1,3 +1,6 @@
|
||||||
|
[Clock]
|
||||||
|
TimeFormat=%T
|
||||||
|
|
||||||
[QuickLaunch]
|
[QuickLaunch]
|
||||||
Browser=Browser.af
|
Browser=Browser.af
|
||||||
Terminal=Terminal.af
|
Terminal=Terminal.af
|
||||||
|
|
|
@ -5,12 +5,16 @@ serenity_component(
|
||||||
)
|
)
|
||||||
|
|
||||||
compile_gml(ClockSettingsWidget.gml ClockSettingsWidgetGML.h clock_settings_widget_gml)
|
compile_gml(ClockSettingsWidget.gml ClockSettingsWidgetGML.h clock_settings_widget_gml)
|
||||||
|
compile_gml(TimeZoneSettingsWidget.gml TimeZoneSettingsWidgetGML.h time_zone_settings_widget_gml)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
main.cpp
|
main.cpp
|
||||||
ClockSettingsWidget.cpp
|
ClockSettingsWidget.cpp
|
||||||
ClockSettingsWidget.h
|
ClockSettingsWidget.h
|
||||||
ClockSettingsWidgetGML.h
|
ClockSettingsWidgetGML.h
|
||||||
|
TimeZoneSettingsWidget.cpp
|
||||||
|
TimeZoneSettingsWidget.h
|
||||||
|
TimeZoneSettingsWidgetGML.h
|
||||||
)
|
)
|
||||||
|
|
||||||
serenity_app(ClockSettings ICON app-analog-clock) # FIXME: Create a ClockSettings icon.
|
serenity_app(ClockSettings ICON app-analog-clock) # FIXME: Create a ClockSettings icon.
|
||||||
|
|
|
@ -1,165 +1,49 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
* Copyright (c) 2022, cflip <cflip@cflip.net>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ClockSettingsWidget.h"
|
#include "ClockSettingsWidget.h"
|
||||||
#include <AK/Time.h>
|
|
||||||
#include <Applications/ClockSettings/ClockSettingsWidgetGML.h>
|
#include <Applications/ClockSettings/ClockSettingsWidgetGML.h>
|
||||||
#include <LibGUI/ComboBox.h>
|
#include <LibConfig/Client.h>
|
||||||
#include <LibGUI/Event.h>
|
#include <LibGUI/RadioButton.h>
|
||||||
#include <LibGUI/ImageWidget.h>
|
#include <LibGUI/TextBox.h>
|
||||||
#include <LibGUI/ItemListModel.h>
|
|
||||||
#include <LibGUI/Label.h>
|
|
||||||
#include <LibGUI/Layout.h>
|
|
||||||
#include <LibGUI/Margins.h>
|
|
||||||
#include <LibGUI/Painter.h>
|
|
||||||
#include <LibGfx/Palette.h>
|
|
||||||
#include <LibTimeZone/TimeZone.h>
|
|
||||||
#include <LibUnicode/DateTimeFormat.h>
|
|
||||||
#include <LibUnicode/Locale.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <spawn.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
using StringViewListModel = GUI::ItemListModel<StringView, Span<StringView const>>;
|
|
||||||
|
|
||||||
static constexpr auto PI_OVER_180 = M_PIf32 / 180.0f;
|
|
||||||
static constexpr auto PI_OVER_4 = M_PIf32 / 4.0f;
|
|
||||||
static constexpr auto TAU = M_PIf32 * 2.0f;
|
|
||||||
|
|
||||||
// The map as stored on disk is a valid Mercadian projected map. But it has quite a bit of dead space that
|
|
||||||
// we can remove. This makes the map non-Mercadian, so we need to adjust our math based on what we removed.
|
|
||||||
static constexpr auto TIME_ZONE_MAP_NORTHERN_TRIM = 78;
|
|
||||||
static constexpr auto TIME_ZONE_MAP_SOUTHERN_TRIM = 50;
|
|
||||||
|
|
||||||
static constexpr auto TIME_ZONE_TEXT_WIDTH = 210;
|
|
||||||
static constexpr auto TIME_ZONE_TEXT_HEIGHT = 40;
|
|
||||||
static constexpr auto TIME_ZONE_TEXT_PADDING = 5;
|
|
||||||
static constexpr auto TIME_ZONE_TEXT_COLOR = Gfx::Color::from_rgb(0xeaf688);
|
|
||||||
|
|
||||||
ClockSettingsWidget::ClockSettingsWidget()
|
ClockSettingsWidget::ClockSettingsWidget()
|
||||||
{
|
{
|
||||||
load_from_gml(clock_settings_widget_gml);
|
load_from_gml(clock_settings_widget_gml);
|
||||||
|
|
||||||
static auto time_zones = TimeZone::all_time_zones();
|
m_24_hour_radio = *find_descendant_of_type_named<GUI::RadioButton>("24hour_radio");
|
||||||
m_time_zone = TimeZone::system_time_zone();
|
auto& twelve_hour_radio = *find_descendant_of_type_named<GUI::RadioButton>("12hour_radio");
|
||||||
|
auto& custom_radio = *find_descendant_of_type_named<GUI::RadioButton>("custom_radio");
|
||||||
|
custom_radio.set_checked(true);
|
||||||
|
|
||||||
m_time_zone_combo_box = *find_descendant_of_type_named<GUI::ComboBox>("time_zone_input");
|
m_custom_format_input = *find_descendant_of_type_named<GUI::TextBox>("custom_format_input");
|
||||||
m_time_zone_combo_box->set_only_allow_values_from_model(true);
|
m_custom_format_input->set_text(Config::read_string("Taskbar", "Clock", "TimeFormat"));
|
||||||
m_time_zone_combo_box->set_model(*StringViewListModel::create(time_zones));
|
|
||||||
m_time_zone_combo_box->set_text(m_time_zone);
|
|
||||||
|
|
||||||
auto time_zone_map_bitmap = Gfx::Bitmap::try_load_from_file("/res/graphics/map.png"sv).release_value_but_fixme_should_propagate_errors();
|
m_24_hour_radio->on_checked = [&](bool) {
|
||||||
auto time_zone_rect = time_zone_map_bitmap->rect().shrunken(TIME_ZONE_MAP_NORTHERN_TRIM, 0, TIME_ZONE_MAP_SOUTHERN_TRIM, 0);
|
m_custom_format_input->set_enabled(false);
|
||||||
time_zone_map_bitmap = time_zone_map_bitmap->cropped(time_zone_rect).release_value_but_fixme_should_propagate_errors();
|
m_custom_format_input->set_text("%T");
|
||||||
|
};
|
||||||
|
|
||||||
m_time_zone_map = *find_descendant_of_type_named<GUI::ImageWidget>("time_zone_map");
|
twelve_hour_radio.on_checked = [&](bool) {
|
||||||
m_time_zone_map->set_bitmap(time_zone_map_bitmap);
|
m_custom_format_input->set_enabled(false);
|
||||||
|
m_custom_format_input->set_text("%I:%M %p");
|
||||||
|
};
|
||||||
|
|
||||||
auto time_zone_marker = Gfx::Bitmap::try_load_from_file("/res/icons/32x32/ladyball.png").release_value_but_fixme_should_propagate_errors();
|
custom_radio.on_checked = [&](bool) {
|
||||||
m_time_zone_marker = time_zone_marker->scaled(0.75f, 0.75f).release_value_but_fixme_should_propagate_errors();
|
m_custom_format_input->set_enabled(true);
|
||||||
|
};
|
||||||
set_time_zone_location();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClockSettingsWidget::second_paint_event(GUI::PaintEvent& event)
|
|
||||||
{
|
|
||||||
GUI::Widget::second_paint_event(event);
|
|
||||||
|
|
||||||
if (!m_time_zone_location.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
GUI::Painter painter(*this);
|
|
||||||
painter.add_clip_rect(event.rect());
|
|
||||||
painter.add_clip_rect(m_time_zone_map->relative_rect());
|
|
||||||
|
|
||||||
auto x = m_time_zone_map->x() + m_time_zone_map->parent_widget()->layout()->margins().left();
|
|
||||||
auto y = m_time_zone_map->y() + m_time_zone_map->parent_widget()->layout()->margins().top();
|
|
||||||
|
|
||||||
auto point = m_time_zone_location->to_type<int>().translated(x, y);
|
|
||||||
point.translate_by(-m_time_zone_marker->width() / 2, -m_time_zone_marker->height() / 2);
|
|
||||||
painter.blit(point, *m_time_zone_marker, rect());
|
|
||||||
|
|
||||||
point = m_time_zone_location->to_type<int>().translated(x, y);
|
|
||||||
point.translate_by(0, -TIME_ZONE_TEXT_HEIGHT / 2);
|
|
||||||
|
|
||||||
if (point.x() <= (m_time_zone_map->width() / 2))
|
|
||||||
point.translate_by(m_time_zone_marker->width() / 2 + TIME_ZONE_TEXT_PADDING, 0);
|
|
||||||
else
|
|
||||||
point.translate_by(-m_time_zone_marker->width() / 2 - TIME_ZONE_TEXT_PADDING - TIME_ZONE_TEXT_WIDTH, 0);
|
|
||||||
|
|
||||||
auto text_area = Gfx::IntRect { point.x(), point.y(), TIME_ZONE_TEXT_WIDTH, TIME_ZONE_TEXT_HEIGHT };
|
|
||||||
painter.draw_rect(text_area, palette().active_window_border1());
|
|
||||||
|
|
||||||
text_area.shrink(2, 2);
|
|
||||||
painter.fill_rect(text_area, TIME_ZONE_TEXT_COLOR);
|
|
||||||
painter.draw_text(text_area, m_time_zone_text, Gfx::TextAlignment::Center);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClockSettingsWidget::reset_default_values()
|
|
||||||
{
|
|
||||||
m_time_zone = "UTC"sv;
|
|
||||||
m_time_zone_combo_box->set_text(m_time_zone);
|
|
||||||
m_time_zone_location.clear();
|
|
||||||
|
|
||||||
set_time_zone();
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClockSettingsWidget::apply_settings()
|
void ClockSettingsWidget::apply_settings()
|
||||||
{
|
{
|
||||||
m_time_zone = m_time_zone_combo_box->text();
|
Config::write_string("Taskbar", "Clock", "TimeFormat", m_custom_format_input->text());
|
||||||
|
|
||||||
set_time_zone_location();
|
|
||||||
set_time_zone();
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClockSettingsWidget::set_time_zone_location()
|
void ClockSettingsWidget::reset_default_values()
|
||||||
{
|
{
|
||||||
m_time_zone_location = compute_time_zone_location();
|
m_24_hour_radio->set_checked(true);
|
||||||
|
Config::write_string("Taskbar", "Clock", "TimeFormat", "%T");
|
||||||
auto locale = Unicode::default_locale();
|
|
||||||
auto now = AK::Time::now_realtime();
|
|
||||||
|
|
||||||
auto name = Unicode::format_time_zone(locale, m_time_zone, Unicode::CalendarPatternStyle::Long, now);
|
|
||||||
auto offset = Unicode::format_time_zone(locale, m_time_zone, Unicode::CalendarPatternStyle::LongOffset, now);
|
|
||||||
|
|
||||||
m_time_zone_text = String::formatted("{}\n({})", name, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Mercator_projection#Derivation
|
|
||||||
Optional<Gfx::FloatPoint> ClockSettingsWidget::compute_time_zone_location() const
|
|
||||||
{
|
|
||||||
auto location = TimeZone::get_time_zone_location(m_time_zone);
|
|
||||||
if (!location.has_value())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
auto latitude = location->latitude.decimal_coordinate();
|
|
||||||
auto longitude = location->longitude.decimal_coordinate();
|
|
||||||
|
|
||||||
auto rect = m_time_zone_map->bitmap()->rect().to_type<float>();
|
|
||||||
|
|
||||||
latitude = logf(tanf(PI_OVER_4 + (latitude * PI_OVER_180 / 2.0f)));
|
|
||||||
|
|
||||||
auto mercadian_x = (longitude + 180.0f) * (rect.width() / 360.0f);
|
|
||||||
auto mercadian_y = (rect.height() / 2.0f) - (rect.width() * latitude / TAU);
|
|
||||||
|
|
||||||
mercadian_y -= TIME_ZONE_MAP_NORTHERN_TRIM / 2;
|
|
||||||
mercadian_y += TIME_ZONE_MAP_SOUTHERN_TRIM / 2;
|
|
||||||
|
|
||||||
return Gfx::FloatPoint { mercadian_x, mercadian_y };
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClockSettingsWidget::set_time_zone() const
|
|
||||||
{
|
|
||||||
pid_t child_pid = 0;
|
|
||||||
char const* argv[] = { "/bin/timezone", m_time_zone.characters(), nullptr };
|
|
||||||
|
|
||||||
if ((errno = posix_spawn(&child_pid, "/bin/timezone", nullptr, nullptr, const_cast<char**>(argv), environ))) {
|
|
||||||
perror("posix_spawn");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,37 +6,42 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@GUI::GroupBox {
|
@GUI::GroupBox {
|
||||||
title: "Time Zone Settings"
|
title: "Time Format"
|
||||||
|
shrink_to_fit: false
|
||||||
|
fixed_height: 160
|
||||||
layout: @GUI::VerticalBoxLayout {
|
layout: @GUI::VerticalBoxLayout {
|
||||||
margins: [16, 8, 8]
|
margins: [16, 8, 8]
|
||||||
spacing: 16
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GUI::Label {
|
@GUI::Label {
|
||||||
text: "Change the system's time zone used for the clock and other applications."
|
text: "Set the date/time format used by the taskbar clock."
|
||||||
text_alignment: "TopLeft"
|
text_alignment: "TopLeft"
|
||||||
}
|
}
|
||||||
|
|
||||||
@GUI::Widget {
|
@GUI::Widget {
|
||||||
layout: @GUI::HorizontalBoxLayout {
|
shrink_to_fit: true
|
||||||
|
layout: @GUI::VerticalBoxLayout {
|
||||||
spacing: 4
|
spacing: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
@GUI::Label {
|
@GUI::RadioButton {
|
||||||
text: "Time Zone:"
|
name: "24hour_radio"
|
||||||
fixed_width: 80
|
text: "24-hour (12:34:56)"
|
||||||
name: "time_zone_label"
|
|
||||||
text_alignment: "CenterLeft"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GUI::ComboBox {
|
@GUI::RadioButton {
|
||||||
name: "time_zone_input"
|
name: "12hour_radio"
|
||||||
|
text: "12-hour (12:34 a.m)"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@GUI::ImageWidget {
|
@GUI::RadioButton {
|
||||||
name: "time_zone_map"
|
name: "custom_radio"
|
||||||
auto_resize: true
|
text: "Custom:"
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::TextBox {
|
||||||
|
name: "custom_format_input"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
* Copyright (c) 2022, cflip <cflip@cflip.net>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/Optional.h>
|
|
||||||
#include <AK/RefPtr.h>
|
#include <AK/RefPtr.h>
|
||||||
#include <AK/String.h>
|
|
||||||
#include <LibGUI/SettingsWindow.h>
|
#include <LibGUI/SettingsWindow.h>
|
||||||
#include <LibGUI/TextEditor.h>
|
|
||||||
#include <LibGUI/Window.h>
|
|
||||||
|
|
||||||
class ClockSettingsWidget final : public GUI::SettingsWindow::Tab {
|
class ClockSettingsWidget final : public GUI::SettingsWindow::Tab {
|
||||||
C_OBJECT(ClockSettingsWidget)
|
C_OBJECT(ClockSettingsWidget)
|
||||||
|
@ -19,20 +15,11 @@ class ClockSettingsWidget final : public GUI::SettingsWindow::Tab {
|
||||||
private:
|
private:
|
||||||
ClockSettingsWidget();
|
ClockSettingsWidget();
|
||||||
|
|
||||||
virtual void second_paint_event(GUI::PaintEvent&) override;
|
|
||||||
|
|
||||||
virtual void apply_settings() override;
|
virtual void apply_settings() override;
|
||||||
virtual void reset_default_values() override;
|
virtual void reset_default_values() override;
|
||||||
|
|
||||||
void set_time_zone_location();
|
RefPtr<GUI::RadioButton> m_24_hour_radio;
|
||||||
Optional<Gfx::FloatPoint> compute_time_zone_location() const;
|
RefPtr<GUI::TextBox> m_custom_format_input;
|
||||||
void set_time_zone() const;
|
|
||||||
|
|
||||||
String m_time_zone;
|
String m_date_format;
|
||||||
RefPtr<GUI::ComboBox> m_time_zone_combo_box;
|
|
||||||
RefPtr<GUI::ImageWidget> m_time_zone_map;
|
|
||||||
RefPtr<Gfx::Bitmap> m_time_zone_marker;
|
|
||||||
|
|
||||||
Optional<Gfx::FloatPoint> m_time_zone_location;
|
|
||||||
String m_time_zone_text;
|
|
||||||
};
|
};
|
||||||
|
|
165
Userland/Applications/ClockSettings/TimeZoneSettingsWidget.cpp
Normal file
165
Userland/Applications/ClockSettings/TimeZoneSettingsWidget.cpp
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TimeZoneSettingsWidget.h"
|
||||||
|
#include <AK/Time.h>
|
||||||
|
#include <Applications/ClockSettings/TimeZoneSettingsWidgetGML.h>
|
||||||
|
#include <LibGUI/ComboBox.h>
|
||||||
|
#include <LibGUI/Event.h>
|
||||||
|
#include <LibGUI/ImageWidget.h>
|
||||||
|
#include <LibGUI/ItemListModel.h>
|
||||||
|
#include <LibGUI/Label.h>
|
||||||
|
#include <LibGUI/Layout.h>
|
||||||
|
#include <LibGUI/Margins.h>
|
||||||
|
#include <LibGUI/Painter.h>
|
||||||
|
#include <LibGfx/Palette.h>
|
||||||
|
#include <LibTimeZone/TimeZone.h>
|
||||||
|
#include <LibUnicode/DateTimeFormat.h>
|
||||||
|
#include <LibUnicode/Locale.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <spawn.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
using StringViewListModel = GUI::ItemListModel<StringView, Span<StringView const>>;
|
||||||
|
|
||||||
|
static constexpr auto PI_OVER_180 = M_PIf32 / 180.0f;
|
||||||
|
static constexpr auto PI_OVER_4 = M_PIf32 / 4.0f;
|
||||||
|
static constexpr auto TAU = M_PIf32 * 2.0f;
|
||||||
|
|
||||||
|
// The map as stored on disk is a valid Mercadian projected map. But it has quite a bit of dead space that
|
||||||
|
// we can remove. This makes the map non-Mercadian, so we need to adjust our math based on what we removed.
|
||||||
|
static constexpr auto TIME_ZONE_MAP_NORTHERN_TRIM = 78;
|
||||||
|
static constexpr auto TIME_ZONE_MAP_SOUTHERN_TRIM = 50;
|
||||||
|
|
||||||
|
static constexpr auto TIME_ZONE_TEXT_WIDTH = 210;
|
||||||
|
static constexpr auto TIME_ZONE_TEXT_HEIGHT = 40;
|
||||||
|
static constexpr auto TIME_ZONE_TEXT_PADDING = 5;
|
||||||
|
static constexpr auto TIME_ZONE_TEXT_COLOR = Gfx::Color::from_rgb(0xeaf688);
|
||||||
|
|
||||||
|
TimeZoneSettingsWidget::TimeZoneSettingsWidget()
|
||||||
|
{
|
||||||
|
load_from_gml(time_zone_settings_widget_gml);
|
||||||
|
|
||||||
|
static auto time_zones = TimeZone::all_time_zones();
|
||||||
|
m_time_zone = TimeZone::system_time_zone();
|
||||||
|
|
||||||
|
m_time_zone_combo_box = *find_descendant_of_type_named<GUI::ComboBox>("time_zone_input");
|
||||||
|
m_time_zone_combo_box->set_only_allow_values_from_model(true);
|
||||||
|
m_time_zone_combo_box->set_model(*StringViewListModel::create(time_zones));
|
||||||
|
m_time_zone_combo_box->set_text(m_time_zone);
|
||||||
|
|
||||||
|
auto time_zone_map_bitmap = Gfx::Bitmap::try_load_from_file("/res/graphics/map.png"sv).release_value_but_fixme_should_propagate_errors();
|
||||||
|
auto time_zone_rect = time_zone_map_bitmap->rect().shrunken(TIME_ZONE_MAP_NORTHERN_TRIM, 0, TIME_ZONE_MAP_SOUTHERN_TRIM, 0);
|
||||||
|
time_zone_map_bitmap = time_zone_map_bitmap->cropped(time_zone_rect).release_value_but_fixme_should_propagate_errors();
|
||||||
|
|
||||||
|
m_time_zone_map = *find_descendant_of_type_named<GUI::ImageWidget>("time_zone_map");
|
||||||
|
m_time_zone_map->set_bitmap(time_zone_map_bitmap);
|
||||||
|
|
||||||
|
auto time_zone_marker = Gfx::Bitmap::try_load_from_file("/res/icons/32x32/ladyball.png").release_value_but_fixme_should_propagate_errors();
|
||||||
|
m_time_zone_marker = time_zone_marker->scaled(0.75f, 0.75f).release_value_but_fixme_should_propagate_errors();
|
||||||
|
|
||||||
|
set_time_zone_location();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeZoneSettingsWidget::second_paint_event(GUI::PaintEvent& event)
|
||||||
|
{
|
||||||
|
GUI::Widget::second_paint_event(event);
|
||||||
|
|
||||||
|
if (!m_time_zone_location.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
GUI::Painter painter(*this);
|
||||||
|
painter.add_clip_rect(event.rect());
|
||||||
|
painter.add_clip_rect(m_time_zone_map->relative_rect());
|
||||||
|
|
||||||
|
auto x = m_time_zone_map->x() + m_time_zone_map->parent_widget()->layout()->margins().left();
|
||||||
|
auto y = m_time_zone_map->y() + m_time_zone_map->parent_widget()->layout()->margins().top();
|
||||||
|
|
||||||
|
auto point = m_time_zone_location->to_type<int>().translated(x, y);
|
||||||
|
point.translate_by(-m_time_zone_marker->width() / 2, -m_time_zone_marker->height() / 2);
|
||||||
|
painter.blit(point, *m_time_zone_marker, rect());
|
||||||
|
|
||||||
|
point = m_time_zone_location->to_type<int>().translated(x, y);
|
||||||
|
point.translate_by(0, -TIME_ZONE_TEXT_HEIGHT / 2);
|
||||||
|
|
||||||
|
if (point.x() <= (m_time_zone_map->width() / 2))
|
||||||
|
point.translate_by(m_time_zone_marker->width() / 2 + TIME_ZONE_TEXT_PADDING, 0);
|
||||||
|
else
|
||||||
|
point.translate_by(-m_time_zone_marker->width() / 2 - TIME_ZONE_TEXT_PADDING - TIME_ZONE_TEXT_WIDTH, 0);
|
||||||
|
|
||||||
|
auto text_area = Gfx::IntRect { point.x(), point.y(), TIME_ZONE_TEXT_WIDTH, TIME_ZONE_TEXT_HEIGHT };
|
||||||
|
painter.draw_rect(text_area, palette().active_window_border1());
|
||||||
|
|
||||||
|
text_area.shrink(2, 2);
|
||||||
|
painter.fill_rect(text_area, TIME_ZONE_TEXT_COLOR);
|
||||||
|
painter.draw_text(text_area, m_time_zone_text, Gfx::TextAlignment::Center);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeZoneSettingsWidget::reset_default_values()
|
||||||
|
{
|
||||||
|
m_time_zone = "UTC"sv;
|
||||||
|
m_time_zone_combo_box->set_text(m_time_zone);
|
||||||
|
m_time_zone_location.clear();
|
||||||
|
|
||||||
|
set_time_zone();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeZoneSettingsWidget::apply_settings()
|
||||||
|
{
|
||||||
|
m_time_zone = m_time_zone_combo_box->text();
|
||||||
|
|
||||||
|
set_time_zone_location();
|
||||||
|
set_time_zone();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeZoneSettingsWidget::set_time_zone_location()
|
||||||
|
{
|
||||||
|
m_time_zone_location = compute_time_zone_location();
|
||||||
|
|
||||||
|
auto locale = Unicode::default_locale();
|
||||||
|
auto now = AK::Time::now_realtime();
|
||||||
|
|
||||||
|
auto name = Unicode::format_time_zone(locale, m_time_zone, Unicode::CalendarPatternStyle::Long, now);
|
||||||
|
auto offset = Unicode::format_time_zone(locale, m_time_zone, Unicode::CalendarPatternStyle::LongOffset, now);
|
||||||
|
|
||||||
|
m_time_zone_text = String::formatted("{}\n({})", name, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Mercator_projection#Derivation
|
||||||
|
Optional<Gfx::FloatPoint> TimeZoneSettingsWidget::compute_time_zone_location() const
|
||||||
|
{
|
||||||
|
auto location = TimeZone::get_time_zone_location(m_time_zone);
|
||||||
|
if (!location.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto latitude = location->latitude.decimal_coordinate();
|
||||||
|
auto longitude = location->longitude.decimal_coordinate();
|
||||||
|
|
||||||
|
auto rect = m_time_zone_map->bitmap()->rect().to_type<float>();
|
||||||
|
|
||||||
|
latitude = logf(tanf(PI_OVER_4 + (latitude * PI_OVER_180 / 2.0f)));
|
||||||
|
|
||||||
|
auto mercadian_x = (longitude + 180.0f) * (rect.width() / 360.0f);
|
||||||
|
auto mercadian_y = (rect.height() / 2.0f) - (rect.width() * latitude / TAU);
|
||||||
|
|
||||||
|
mercadian_y -= TIME_ZONE_MAP_NORTHERN_TRIM / 2;
|
||||||
|
mercadian_y += TIME_ZONE_MAP_SOUTHERN_TRIM / 2;
|
||||||
|
|
||||||
|
return Gfx::FloatPoint { mercadian_x, mercadian_y };
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimeZoneSettingsWidget::set_time_zone() const
|
||||||
|
{
|
||||||
|
pid_t child_pid = 0;
|
||||||
|
char const* argv[] = { "/bin/timezone", m_time_zone.characters(), nullptr };
|
||||||
|
|
||||||
|
if ((errno = posix_spawn(&child_pid, "/bin/timezone", nullptr, nullptr, const_cast<char**>(argv), environ))) {
|
||||||
|
perror("posix_spawn");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
@GUI::Frame {
|
||||||
|
fill_with_background_color: true
|
||||||
|
layout: @GUI::VerticalBoxLayout {
|
||||||
|
margins: [10]
|
||||||
|
spacing: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::GroupBox {
|
||||||
|
title: "Time Zone Settings"
|
||||||
|
layout: @GUI::VerticalBoxLayout {
|
||||||
|
margins: [16, 8, 8]
|
||||||
|
spacing: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Change the system's time zone used for the clock and other applications."
|
||||||
|
text_alignment: "TopLeft"
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Widget {
|
||||||
|
layout: @GUI::HorizontalBoxLayout {
|
||||||
|
spacing: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Label {
|
||||||
|
text: "Time Zone:"
|
||||||
|
fixed_width: 80
|
||||||
|
name: "time_zone_label"
|
||||||
|
text_alignment: "CenterLeft"
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::ComboBox {
|
||||||
|
name: "time_zone_input"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::ImageWidget {
|
||||||
|
name: "time_zone_map"
|
||||||
|
auto_resize: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
Userland/Applications/ClockSettings/TimeZoneSettingsWidget.h
Normal file
38
Userland/Applications/ClockSettings/TimeZoneSettingsWidget.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Optional.h>
|
||||||
|
#include <AK/RefPtr.h>
|
||||||
|
#include <AK/String.h>
|
||||||
|
#include <LibGUI/SettingsWindow.h>
|
||||||
|
#include <LibGUI/TextEditor.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
|
||||||
|
class TimeZoneSettingsWidget final : public GUI::SettingsWindow::Tab {
|
||||||
|
C_OBJECT(TimeZoneSettingsWidget)
|
||||||
|
|
||||||
|
private:
|
||||||
|
TimeZoneSettingsWidget();
|
||||||
|
|
||||||
|
virtual void second_paint_event(GUI::PaintEvent&) override;
|
||||||
|
|
||||||
|
virtual void apply_settings() override;
|
||||||
|
virtual void reset_default_values() override;
|
||||||
|
|
||||||
|
void set_time_zone_location();
|
||||||
|
Optional<Gfx::FloatPoint> compute_time_zone_location() const;
|
||||||
|
void set_time_zone() const;
|
||||||
|
|
||||||
|
String m_time_zone;
|
||||||
|
RefPtr<GUI::ComboBox> m_time_zone_combo_box;
|
||||||
|
RefPtr<GUI::ImageWidget> m_time_zone_map;
|
||||||
|
RefPtr<Gfx::Bitmap> m_time_zone_marker;
|
||||||
|
|
||||||
|
Optional<Gfx::FloatPoint> m_time_zone_location;
|
||||||
|
String m_time_zone_text;
|
||||||
|
};
|
|
@ -5,6 +5,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ClockSettingsWidget.h"
|
#include "ClockSettingsWidget.h"
|
||||||
|
#include "TimeZoneSettingsWidget.h"
|
||||||
|
#include <LibConfig/Client.h>
|
||||||
#include <LibCore/System.h>
|
#include <LibCore/System.h>
|
||||||
#include <LibGUI/Application.h>
|
#include <LibGUI/Application.h>
|
||||||
#include <LibGUI/Icon.h>
|
#include <LibGUI/Icon.h>
|
||||||
|
@ -17,6 +19,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
||||||
auto app = TRY(GUI::Application::try_create(arguments));
|
auto app = TRY(GUI::Application::try_create(arguments));
|
||||||
|
|
||||||
|
Config::pledge_domain("Taskbar");
|
||||||
TRY(Core::System::pledge("stdio rpath recvfd sendfd proc exec"));
|
TRY(Core::System::pledge("stdio rpath recvfd sendfd proc exec"));
|
||||||
TRY(Core::System::unveil("/res", "r"));
|
TRY(Core::System::unveil("/res", "r"));
|
||||||
TRY(Core::System::unveil("/bin/timezone", "x"));
|
TRY(Core::System::unveil("/bin/timezone", "x"));
|
||||||
|
@ -27,6 +30,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
||||||
auto window = TRY(GUI::SettingsWindow::create("Clock Settings", GUI::SettingsWindow::ShowDefaultsButton::Yes));
|
auto window = TRY(GUI::SettingsWindow::create("Clock Settings", GUI::SettingsWindow::ShowDefaultsButton::Yes));
|
||||||
(void)TRY(window->add_tab<ClockSettingsWidget>("Clock"));
|
(void)TRY(window->add_tab<ClockSettingsWidget>("Clock"));
|
||||||
|
(void)TRY(window->add_tab<TimeZoneSettingsWidget>("Time Zone"));
|
||||||
window->set_icon(app_icon.bitmap_for_size(16));
|
window->set_icon(app_icon.bitmap_for_size(16));
|
||||||
window->resize(540, 570);
|
window->resize(540, 570);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ClockWidget.h"
|
#include "ClockWidget.h"
|
||||||
|
#include <LibConfig/Client.h>
|
||||||
#include <LibCore/Process.h>
|
#include <LibCore/Process.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
#include <LibGUI/SeparatorWidget.h>
|
#include <LibGUI/SeparatorWidget.h>
|
||||||
|
@ -20,9 +21,7 @@ ClockWidget::ClockWidget()
|
||||||
set_frame_shadow(Gfx::FrameShadow::Sunken);
|
set_frame_shadow(Gfx::FrameShadow::Sunken);
|
||||||
set_frame_thickness(1);
|
set_frame_thickness(1);
|
||||||
|
|
||||||
m_time_width = font().width("22:22:22");
|
update_format(Config::read_string("Taskbar", "Clock", "TimeFormat", "%T"));
|
||||||
|
|
||||||
set_fixed_size(m_time_width + 20, 21);
|
|
||||||
|
|
||||||
m_timer = add<Core::Timer>(1000, [this] {
|
m_timer = add<Core::Timer>(1000, [this] {
|
||||||
static time_t last_update_time;
|
static time_t last_update_time;
|
||||||
|
@ -159,10 +158,17 @@ ClockWidget::ClockWidget()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClockWidget::update_format(String const& format)
|
||||||
|
{
|
||||||
|
m_time_format = format;
|
||||||
|
m_time_width = font().width(Core::DateTime::create(122, 2, 22, 22, 22, 22).to_string(format));
|
||||||
|
set_fixed_size(m_time_width + 20, 21);
|
||||||
|
}
|
||||||
|
|
||||||
void ClockWidget::paint_event(GUI::PaintEvent& event)
|
void ClockWidget::paint_event(GUI::PaintEvent& event)
|
||||||
{
|
{
|
||||||
GUI::Frame::paint_event(event);
|
GUI::Frame::paint_event(event);
|
||||||
auto time_text = Core::DateTime::now().to_string("%T");
|
auto time_text = Core::DateTime::now().to_string(m_time_format);
|
||||||
GUI::Painter painter(*this);
|
GUI::Painter painter(*this);
|
||||||
painter.add_clip_rect(frame_inner_rect());
|
painter.add_clip_rect(frame_inner_rect());
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ class ClockWidget final : public GUI::Frame {
|
||||||
public:
|
public:
|
||||||
virtual ~ClockWidget() override = default;
|
virtual ~ClockWidget() override = default;
|
||||||
|
|
||||||
|
void update_format(String const&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ClockWidget();
|
ClockWidget();
|
||||||
|
|
||||||
|
@ -42,6 +44,7 @@ private:
|
||||||
void position_calendar_window();
|
void position_calendar_window();
|
||||||
void jump_to_current_date();
|
void jump_to_current_date();
|
||||||
|
|
||||||
|
String m_time_format;
|
||||||
RefPtr<GUI::Window> m_calendar_window;
|
RefPtr<GUI::Window> m_calendar_window;
|
||||||
RefPtr<GUI::Calendar> m_calendar;
|
RefPtr<GUI::Calendar> m_calendar;
|
||||||
RefPtr<GUI::Button> m_next_date;
|
RefPtr<GUI::Button> m_next_date;
|
||||||
|
|
|
@ -85,7 +85,7 @@ TaskbarWindow::TaskbarWindow(NonnullRefPtr<GUI::Menu> start_menu)
|
||||||
m_applet_area_container->set_frame_shape(Gfx::FrameShape::Box);
|
m_applet_area_container->set_frame_shape(Gfx::FrameShape::Box);
|
||||||
m_applet_area_container->set_frame_shadow(Gfx::FrameShadow::Sunken);
|
m_applet_area_container->set_frame_shadow(Gfx::FrameShadow::Sunken);
|
||||||
|
|
||||||
main_widget.add<Taskbar::ClockWidget>();
|
m_clock_widget = main_widget.add<Taskbar::ClockWidget>();
|
||||||
|
|
||||||
m_show_desktop_button = GUI::Button::construct();
|
m_show_desktop_button = GUI::Button::construct();
|
||||||
m_show_desktop_button->set_tooltip("Show Desktop");
|
m_show_desktop_button->set_tooltip("Show Desktop");
|
||||||
|
@ -99,6 +99,15 @@ TaskbarWindow::TaskbarWindow(NonnullRefPtr<GUI::Menu> start_menu)
|
||||||
m_assistant_app_file = Desktop::AppFile::open(af_path);
|
m_assistant_app_file = Desktop::AppFile::open(af_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TaskbarWindow::config_string_did_change(String const& domain, String const& group, String const& key, String const& value)
|
||||||
|
{
|
||||||
|
VERIFY(domain == "Taskbar");
|
||||||
|
if (group == "Clock" && key == "TimeFormat") {
|
||||||
|
m_clock_widget->update_format(value);
|
||||||
|
update_applet_area();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TaskbarWindow::show_desktop_button_clicked(unsigned)
|
void TaskbarWindow::show_desktop_button_clicked(unsigned)
|
||||||
{
|
{
|
||||||
GUI::ConnectionToWindowMangerServer::the().async_toggle_show_desktop();
|
GUI::ConnectionToWindowMangerServer::the().async_toggle_show_desktop();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ClockWidget.h"
|
||||||
#include "WindowList.h"
|
#include "WindowList.h"
|
||||||
#include <LibConfig/Listener.h>
|
#include <LibConfig/Listener.h>
|
||||||
#include <LibDesktop/AppFile.h>
|
#include <LibDesktop/AppFile.h>
|
||||||
|
@ -14,7 +15,8 @@
|
||||||
#include <LibGfx/ShareableBitmap.h>
|
#include <LibGfx/ShareableBitmap.h>
|
||||||
#include <Services/WindowServer/ScreenLayout.h>
|
#include <Services/WindowServer/ScreenLayout.h>
|
||||||
|
|
||||||
class TaskbarWindow final : public GUI::Window {
|
class TaskbarWindow final : public GUI::Window
|
||||||
|
, public Config::Listener {
|
||||||
C_OBJECT(TaskbarWindow);
|
C_OBJECT(TaskbarWindow);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -23,6 +25,8 @@ public:
|
||||||
static int taskbar_height() { return 27; }
|
static int taskbar_height() { return 27; }
|
||||||
static int taskbar_icon_size() { return 16; }
|
static int taskbar_icon_size() { return 16; }
|
||||||
|
|
||||||
|
virtual void config_string_did_change(String const&, String const&, String const&, String const&) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit TaskbarWindow(NonnullRefPtr<GUI::Menu> start_menu);
|
explicit TaskbarWindow(NonnullRefPtr<GUI::Menu> start_menu);
|
||||||
static void show_desktop_button_clicked(unsigned);
|
static void show_desktop_button_clicked(unsigned);
|
||||||
|
@ -53,6 +57,7 @@ private:
|
||||||
RefPtr<GUI::Frame> m_applet_area_container;
|
RefPtr<GUI::Frame> m_applet_area_container;
|
||||||
RefPtr<GUI::Button> m_start_button;
|
RefPtr<GUI::Button> m_start_button;
|
||||||
RefPtr<GUI::Button> m_show_desktop_button;
|
RefPtr<GUI::Button> m_show_desktop_button;
|
||||||
|
RefPtr<Taskbar::ClockWidget> m_clock_widget;
|
||||||
|
|
||||||
RefPtr<Desktop::AppFile> m_assistant_app_file;
|
RefPtr<Desktop::AppFile> m_assistant_app_file;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue