Serenity Operating System
at master 239 lines 9.9 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "ClockWidget.h" 8#include <LibConfig/Client.h> 9#include <LibGUI/Action.h> 10#include <LibGUI/Menu.h> 11#include <LibGUI/Painter.h> 12#include <LibGUI/Process.h> 13#include <LibGUI/SeparatorWidget.h> 14#include <LibGUI/Window.h> 15#include <LibGfx/Bitmap.h> 16#include <LibGfx/Font/FontDatabase.h> 17#include <LibGfx/Palette.h> 18 19namespace Taskbar { 20 21ClockWidget::ClockWidget() 22{ 23 set_frame_shape(Gfx::FrameShape::Box); 24 set_frame_shadow(Gfx::FrameShadow::Sunken); 25 set_frame_thickness(1); 26 27 update_format(Config::read_string("Taskbar"sv, "Clock"sv, "TimeFormat"sv, "%T"sv)); 28 29 m_timer = add<Core::Timer>(1000, [this] { 30 static time_t last_update_time; 31 time_t now = time(nullptr); 32 if (now != last_update_time) { 33 tick_clock(); 34 last_update_time = now; 35 set_tooltip(Core::DateTime::now().to_deprecated_string("%Y-%m-%d"sv)); 36 } 37 }); 38 m_timer->start(); 39 40 m_calendar_window = add<GUI::Window>(window()); 41 m_calendar_window->set_window_type(GUI::WindowType::Popup); 42 m_calendar_window->resize(m_window_size.width(), m_window_size.height()); 43 44 auto root_container = m_calendar_window->set_main_widget<GUI::Frame>().release_value_but_fixme_should_propagate_errors(); 45 root_container->set_fill_with_background_color(true); 46 root_container->set_layout<GUI::VerticalBoxLayout>(GUI::Margins { 2, 0 }, 0); 47 root_container->set_frame_shape(Gfx::FrameShape::Window); 48 49 auto& navigation_container = root_container->add<GUI::Widget>(); 50 navigation_container.set_fixed_height(24); 51 navigation_container.set_layout<GUI::HorizontalBoxLayout>(GUI::Margins { 2 }); 52 53 m_prev_date = navigation_container.add<GUI::Button>(); 54 m_prev_date->set_button_style(Gfx::ButtonStyle::Coolbar); 55 m_prev_date->set_fixed_size(24, 24); 56 m_prev_date->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"sv).release_value_but_fixme_should_propagate_errors()); 57 m_prev_date->on_click = [&](auto) { 58 unsigned view_month = m_calendar->view_month(); 59 unsigned view_year = m_calendar->view_year(); 60 if (m_calendar->mode() == GUI::Calendar::Month) { 61 view_month--; 62 if (m_calendar->view_month() == 1) { 63 view_month = 12; 64 view_year--; 65 } 66 } else { 67 view_year--; 68 } 69 m_calendar->update_tiles(view_year, view_month); 70 if (m_calendar->mode() == GUI::Calendar::Year) 71 m_selected_calendar_button->set_text(m_calendar->formatted_date(GUI::Calendar::YearOnly).release_value_but_fixme_should_propagate_errors()); 72 else 73 m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); 74 }; 75 76 m_selected_calendar_button = navigation_container.add<GUI::Button>(); 77 m_selected_calendar_button->set_button_style(Gfx::ButtonStyle::Coolbar); 78 m_selected_calendar_button->set_fixed_height(24); 79 m_selected_calendar_button->on_click = [&](auto) { 80 m_calendar->toggle_mode(); 81 if (m_calendar->mode() == GUI::Calendar::Year) 82 m_selected_calendar_button->set_text(m_calendar->formatted_date(GUI::Calendar::YearOnly).release_value_but_fixme_should_propagate_errors()); 83 else 84 m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); 85 }; 86 87 m_next_date = navigation_container.add<GUI::Button>(); 88 m_next_date->set_button_style(Gfx::ButtonStyle::Coolbar); 89 m_next_date->set_fixed_size(24, 24); 90 m_next_date->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"sv).release_value_but_fixme_should_propagate_errors()); 91 m_next_date->on_click = [&](auto) { 92 unsigned view_month = m_calendar->view_month(); 93 unsigned view_year = m_calendar->view_year(); 94 if (m_calendar->mode() == GUI::Calendar::Month) { 95 view_month++; 96 if (m_calendar->view_month() == 12) { 97 view_month = 1; 98 view_year++; 99 } 100 } else { 101 view_year++; 102 } 103 m_calendar->update_tiles(view_year, view_month); 104 if (m_calendar->mode() == GUI::Calendar::Year) 105 m_selected_calendar_button->set_text(m_calendar->formatted_date(GUI::Calendar::YearOnly).release_value_but_fixme_should_propagate_errors()); 106 else 107 m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); 108 }; 109 110 auto& separator1 = root_container->add<GUI::HorizontalSeparator>(); 111 separator1.set_fixed_height(2); 112 113 auto& calendar_container = root_container->add<GUI::Widget>(); 114 calendar_container.set_layout<GUI::HorizontalBoxLayout>(GUI::Margins { 2 }); 115 116 m_calendar = calendar_container.add<GUI::Calendar>(); 117 m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); 118 119 m_calendar->on_tile_click = [&] { 120 m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); 121 }; 122 123 m_calendar->on_month_click = [&] { 124 m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); 125 }; 126 127 auto& separator2 = root_container->add<GUI::HorizontalSeparator>(); 128 separator2.set_fixed_height(2); 129 130 auto& settings_container = root_container->add<GUI::Widget>(); 131 settings_container.set_fixed_height(24); 132 settings_container.set_layout<GUI::HorizontalBoxLayout>(GUI::Margins { 2 }); 133 settings_container.add_spacer().release_value_but_fixme_should_propagate_errors(); 134 135 m_jump_to_button = settings_container.add<GUI::Button>(); 136 m_jump_to_button->set_button_style(Gfx::ButtonStyle::Coolbar); 137 m_jump_to_button->set_fixed_size(24, 24); 138 m_jump_to_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/calendar-date.png"sv).release_value_but_fixme_should_propagate_errors()); 139 m_jump_to_button->set_tooltip("Jump to today"); 140 m_jump_to_button->on_click = [this](auto) { 141 jump_to_current_date(); 142 }; 143 144 m_calendar_launcher = settings_container.add<GUI::Button>(); 145 m_calendar_launcher->set_button_style(Gfx::ButtonStyle::Coolbar); 146 m_calendar_launcher->set_fixed_size(24, 24); 147 m_calendar_launcher->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-calendar.png"sv).release_value_but_fixme_should_propagate_errors()); 148 m_calendar_launcher->set_tooltip("Calendar"); 149 m_calendar_launcher->on_click = [this](auto) { 150 GUI::Process::spawn_or_show_error(window(), "/bin/Calendar"sv); 151 }; 152} 153 154void ClockWidget::update_format(DeprecatedString const& format) 155{ 156 m_time_format = format; 157 m_time_width = font().width(Core::DateTime::create(122, 2, 22, 22, 22, 22).to_deprecated_string(format)); 158 set_fixed_size(m_time_width + 20, 21); 159} 160 161void ClockWidget::paint_event(GUI::PaintEvent& event) 162{ 163 GUI::Frame::paint_event(event); 164 auto time_text = Core::DateTime::now().to_deprecated_string(m_time_format); 165 GUI::Painter painter(*this); 166 painter.add_clip_rect(frame_inner_rect()); 167 168 // Render string center-left aligned, but attempt to center the string based on a constant 169 // "ideal" time string (i.e., the same one used to size this widget in the initializer). 170 // This prevents the rest of the string from shifting around while seconds tick. 171 Gfx::Font const& font = Gfx::FontDatabase::default_font(); 172 int const frame_width = frame_thickness(); 173 int const ideal_width = m_time_width; 174 int const widget_width = max_width().as_int(); 175 int const translation_x = (widget_width - ideal_width) / 2 - frame_width; 176 177 painter.draw_text(frame_inner_rect().translated(translation_x, frame_width), time_text, font, Gfx::TextAlignment::CenterLeft, palette().window_text()); 178} 179 180void ClockWidget::mousedown_event(GUI::MouseEvent& event) 181{ 182 if (event.button() != GUI::MouseButton::Primary) { 183 return; 184 } else { 185 if (!m_calendar_window->is_visible()) 186 open(); 187 else 188 close(); 189 } 190} 191 192void ClockWidget::context_menu_event(GUI::ContextMenuEvent& event) 193{ 194 if (!m_context_menu) { 195 m_context_menu = GUI::Menu::construct(); 196 197 auto settings_icon = MUST(Gfx::Bitmap::load_from_file("/res/icons/16x16/settings.png"sv)); 198 auto open_clock_settings_action = GUI::Action::create("Clock &Settings", *settings_icon, [this](auto&) { 199 GUI::Process::spawn_or_show_error(window(), "/bin/ClockSettings"sv, Array { "--open-tab", "clock" }); 200 }); 201 202 m_context_menu->add_action(open_clock_settings_action); 203 } 204 205 m_context_menu->popup(event.screen_position()); 206} 207 208void ClockWidget::open() 209{ 210 jump_to_current_date(); 211 position_calendar_window(); 212 m_calendar_window->show(); 213} 214 215void ClockWidget::close() 216{ 217 m_calendar_window->hide(); 218} 219 220void ClockWidget::position_calendar_window() 221{ 222 constexpr auto taskbar_top_padding { 4 }; 223 m_calendar_window->set_rect( 224 screen_relative_rect().right() - m_calendar_window->width() + 1, 225 screen_relative_rect().top() - taskbar_top_padding - m_calendar_window->height(), 226 m_window_size.width(), 227 m_window_size.height()); 228} 229 230void ClockWidget::jump_to_current_date() 231{ 232 if (m_calendar->mode() == GUI::Calendar::Year) 233 m_calendar->toggle_mode(); 234 m_calendar->set_selected_date(Core::DateTime::now()); 235 m_calendar->update_tiles(Core::DateTime::now().year(), Core::DateTime::now().month()); 236 m_selected_calendar_button->set_text(m_calendar->formatted_date().release_value_but_fixme_should_propagate_errors()); 237} 238 239}