Serenity Operating System
at master 163 lines 6.8 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "GraphWidget.h" 9#include <LibCore/Object.h> 10#include <LibGUI/Application.h> 11#include <LibGUI/Painter.h> 12#include <LibGfx/Font/Font.h> 13#include <LibGfx/Palette.h> 14#include <LibGfx/Path.h> 15#include <LibGfx/SystemTheme.h> 16 17REGISTER_WIDGET(SystemMonitor, GraphWidget) 18 19namespace SystemMonitor { 20 21GraphWidget::GraphWidget() 22{ 23 REGISTER_BOOL_PROPERTY("stack_values", stack_values, set_stack_values); 24} 25 26void GraphWidget::set_stack_values(bool stack_values) 27{ 28 m_stack_values = stack_values; 29 update(); 30} 31 32void GraphWidget::add_value(Vector<u64, 1>&& value) 33{ 34 m_values.enqueue(move(value)); 35 update(); 36} 37 38void GraphWidget::paint_event(GUI::PaintEvent& event) 39{ 40 auto const& system_palette = GUI::Application::the()->palette(); 41 42 GUI::Frame::paint_event(event); 43 GUI::Painter painter(*this); 44 painter.add_clip_rect(event.rect()); 45 painter.add_clip_rect(frame_inner_rect()); 46 painter.fill_rect(event.rect(), palette().base()); 47 48 auto inner_rect = frame_inner_rect(); 49 float scale = (float)inner_rect.height() / (float)m_max; 50 51 if (!m_values.is_empty()) { 52 // Draw one set of values at a time 53 for (size_t k = 0; k < m_value_format.size(); k++) { 54 auto const& format = m_value_format[k]; 55 if (format.graph_color_role == ColorRole::Base) { 56 continue; 57 } 58 auto const& line_color = system_palette.color(format.graph_color_role); 59 auto const& background_color = line_color.with_alpha(0x7f); 60 m_calculated_points.clear_with_capacity(); 61 for (size_t i = 0; i < m_values.size(); i++) { 62 int x = inner_rect.right() - (i * 2) + 1; 63 if (x < 0) 64 break; 65 auto const& current_values = m_values.at(m_values.size() - i - 1); 66 if (current_values.size() <= k) { 67 // Don't have a data point 68 m_calculated_points.append({ -1, -1 }); 69 continue; 70 } 71 float value = current_values[k]; 72 if (m_stack_values) { 73 for (size_t l = k + 1; l < current_values.size(); l++) 74 value += current_values[l]; 75 } 76 float scaled_value = value * scale; 77 Gfx::IntPoint current_point { x, inner_rect.bottom() - (int)scaled_value }; 78 m_calculated_points.append(current_point); 79 } 80 VERIFY(m_calculated_points.size() <= m_values.size()); 81 if (format.graph_color_role != ColorRole::Base) { 82 // Fill the background for the area we have values for 83 Gfx::Path path; 84 size_t points_in_path = 0; 85 bool started_path = false; 86 Gfx::IntPoint const* current_point = nullptr; 87 Gfx::IntPoint const* first_point = nullptr; 88 auto check_fill_area = [&]() { 89 if (!started_path) 90 return; 91 if (points_in_path > 1) { 92 VERIFY(current_point); 93 VERIFY(first_point); 94 path.line_to({ current_point->x() - 1, inner_rect.bottom() + 1 }); 95 path.line_to({ first_point->x() + 1, inner_rect.bottom() + 1 }); 96 path.close(); 97 painter.fill_path(path, background_color, Gfx::Painter::WindingRule::EvenOdd); 98 } else if (points_in_path == 1 && current_point) { 99 // Can't fill any area, we only have one data point. 100 // Just draw a vertical line as a "fill"... 101 painter.draw_line(*current_point, { current_point->x(), inner_rect.bottom() }, background_color); 102 } 103 path = {}; 104 points_in_path = 0; 105 first_point = nullptr; 106 started_path = false; 107 }; 108 for (size_t i = 0; i < m_calculated_points.size(); i++) { 109 current_point = &m_calculated_points[i]; 110 if (current_point->x() < 0) { 111 check_fill_area(); 112 continue; 113 } 114 if (!started_path) { 115 path.move_to({ current_point->x() + 1, current_point->y() }); 116 points_in_path = 1; 117 first_point = current_point; 118 started_path = true; 119 } else { 120 path.line_to({ current_point->x(), current_point->y() }); 121 points_in_path++; 122 } 123 } 124 check_fill_area(); 125 } 126 if (format.graph_color_role != ColorRole::Base) { 127 // Draw the line for the data points we have 128 Gfx::IntPoint const* previous_point = nullptr; 129 for (size_t i = 0; i < m_calculated_points.size(); i++) { 130 auto const& current_point = m_calculated_points[i]; 131 if (current_point.x() < 0) { 132 previous_point = nullptr; 133 continue; 134 } 135 if (previous_point) 136 painter.draw_line(*previous_point, current_point, line_color); 137 previous_point = &current_point; 138 } 139 } 140 } 141 } 142 143 if (!m_values.is_empty() && !m_value_format.is_empty()) { 144 auto const& current_values = m_values.last(); 145 int y = 0; 146 for (size_t i = 0; i < min(m_value_format.size(), current_values.size()); i++) { 147 auto const& format = m_value_format[i]; 148 auto const& graph_color = system_palette.color(format.graph_color_role); 149 if (!format.text_formatter) 150 continue; 151 auto constrain_rect = inner_rect.shrunken(8, 8); 152 auto text_rect = constrain_rect.translated(0, y).intersected(constrain_rect); 153 text_rect.set_height(font().pixel_size_rounded_up()); 154 auto text = format.text_formatter(current_values[i]); 155 if (format.text_shadow_color != Color::Transparent) 156 painter.draw_text(text_rect.translated(1, 1), text, Gfx::TextAlignment::CenterRight, format.text_shadow_color); 157 painter.draw_text(text_rect, text, Gfx::TextAlignment::CenterRight, graph_color); 158 y += text_rect.height() + 4; 159 } 160 } 161} 162 163}