Skip to content

Commit

Permalink
draft for donut charts implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
GyulyVGC committed Feb 14, 2025
1 parent e2a3ab4 commit a942932
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 54 deletions.
143 changes: 143 additions & 0 deletions src/chart/types/donut_chart.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
use crate::gui::styles::donut::Catalog;
use crate::gui::styles::style_constants::FONT_SIZE_BODY;
use iced::alignment::{Horizontal, Vertical};
use iced::widget::canvas::path::Arc;
use iced::widget::canvas::{Frame, Text};
use iced::widget::text::Shaping;
use iced::widget::{canvas, Canvas};
use iced::{mouse, Font, Point, Radians, Renderer};
use std::f32::consts;

pub struct DonutChart {
percentage: f32,
label: String,
font: Font,
}

impl DonutChart {
pub fn new(percentage: f32, label: impl Into<String>, font: Font) -> Self {
let label = label.into();
Self {
percentage,
label,
font,
}
}
}

impl<Message, Theme: Catalog> canvas::Program<Message, Theme> for DonutChart {
type State = ();

fn draw(
&self,
_: &Self::State,
renderer: &Renderer,
theme: &Theme,
bounds: iced::Rectangle,
_: mouse::Cursor,
) -> Vec<canvas::Geometry> {
let mut frame = Frame::new(renderer, bounds.size());
let center = frame.center();
let radius = frame.width().min(frame.height()) / 2.0;

let start_angle1 = Radians(-consts::FRAC_PI_2);
let end_angle1 = Radians(consts::TAU) * self.percentage / 100.0 + start_angle1;

let style = <Theme as Catalog>::style(theme, &<Theme as Catalog>::default());

// let circle_at_angle = |angle: Radians| {
// canvas::Path::circle(
// Point::new(
// center.x + fixed_radius * angle.0.cos(),
// center.y + fixed_radius * angle.0.sin(),
// ),
// inner_ball_radius,
// )
// };

let circle = canvas::Path::circle(center, radius);

let incoming = canvas::Path::new(|builder| {
builder.arc(Arc {
center,
radius,
start_angle: start_angle1,
end_angle: end_angle1,
});
builder.line_to(center);
builder.close();
});

let outgoing = canvas::Path::new(|builder| {
builder.arc(Arc {
center,
radius,
start_angle: end_angle1,
end_angle: start_angle1 + Radians(consts::PI * 2.0),
});
builder.line_to(center);
builder.close();
});

let inner_circle = canvas::Path::circle(center, radius - 6.0);

frame.fill_text(Text {
content: if self.label.is_empty() {
format!("{:.2}%", self.percentage)
} else {
self.label.clone()
},
position: center,
vertical_alignment: Vertical::Center,
horizontal_alignment: Horizontal::Center,
color: style.text_color,
size: FONT_SIZE_BODY.into(),
font: self.font,
..Default::default()
});
frame.fill(&circle, style.background);
frame.fill(&incoming, style.incoming);
frame.fill(&outgoing, style.outgoing);
frame.fill(&inner_circle, style.rail);

vec![frame.into_geometry()]
}
}

/// Creates a radial progress bar widget.
///
/// This function returns a `Canvas` widget that displays a radial progress bar
/// with a specified percentage and content. If the content is empty, the
/// percentage will be displayed by default.
///
/// # Example
///
/// ```rust
/// use atoms::widgets::radial_progress_bar;
///
/// let progress = radial_progress_bar(0.75, "75% Complete");
/// let default_progress = radial_progress_bar(0.75, "");
/// ```
pub fn donut_chart<Message, Theme: Catalog>(
percentage: f32,
label: impl Into<String>,
font: Font,
) -> Canvas<DonutChart, Message, Theme, Renderer> {
let radius = 95.0;
iced::widget::canvas(DonutChart::new(percentage, label, font))
.width(radius)
.height(radius)
}

/// The status of a [`ProgressBar`].
#[derive(Debug, Clone, Copy)]
pub enum Status {
/// The progress bar is idle.
Idle,
/// The progress bar is currently progressing.
Progressing,
/// The progress bar has finished.
Finished,
/// The progress bar has failed.
Failed,
}
1 change: 1 addition & 0 deletions src/chart/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod chart_type;
pub mod donut_chart;
pub mod traffic_chart;
128 changes: 74 additions & 54 deletions src/gui/pages/overview_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ use iced::widget::scrollable::Direction;
use iced::widget::text::LineHeight;
use iced::widget::tooltip::Position;
use iced::widget::{
button, horizontal_space, lazy, vertical_space, Button, Column, Container, Row, Rule,
button, horizontal_space, lazy, vertical_space, Button, Canvas, Column, Container, Row, Rule,
Scrollable, Space, Text, Tooltip,
};
use iced::Length::{Fill, FillPortion};
use iced::{Alignment, Font, Length, Padding};

use crate::chart::types::donut_chart::{donut_chart, DonutChart};
use crate::countries::country_utils::get_flag_tooltip;
use crate::countries::flags_pictures::FLAGS_WIDTH_BIG;
use crate::gui::components::tab::get_pages_tabs;
Expand All @@ -34,9 +35,10 @@ use crate::report::types::search_parameters::SearchParameters;
use crate::report::types::sort_type::SortType;
use crate::translations::translations::{
active_filters_translation, bytes_chart_translation, error_translation,
filtered_bytes_translation, filtered_packets_translation, network_adapter_translation,
no_addresses_translation, none_translation, of_total_translation, packets_chart_translation,
some_observed_translation, traffic_rate_translation, waiting_translation,
filtered_bytes_translation, filtered_packets_translation, ip_version_translation,
network_adapter_translation, no_addresses_translation, none_translation, of_total_translation,
packets_chart_translation, protocol_translation, some_observed_translation,
traffic_rate_translation, waiting_translation,
};
use crate::translations::translations_2::{
data_representation_translation, dropped_packets_translation, host_translation,
Expand Down Expand Up @@ -464,7 +466,7 @@ fn lazy_col_info<'a>(
let col_data_representation =
col_data_representation(language, font, sniffer.traffic_chart.chart_type);

let col_bytes_packets = col_bytes_packets(
let donut_charts = donut_charts(
language,
dropped,
total,
Expand Down Expand Up @@ -493,7 +495,7 @@ fn lazy_col_info<'a>(
.push(Rule::horizontal(15))
.push(
Scrollable::with_direction(
col_bytes_packets,
donut_charts,
Direction::Vertical(ScrollbarType::properties()),
)
.height(Length::Fill)
Expand Down Expand Up @@ -603,7 +605,7 @@ fn col_data_representation<'a>(
ret_val
}

fn col_bytes_packets<'a>(
fn donut_charts<'a>(
language: Language,
dropped: u32,
total: u128,
Expand All @@ -612,57 +614,75 @@ fn col_bytes_packets<'a>(
font_headers: Font,
sniffer: &Sniffer,
) -> Column<'a, Message, StyleType> {
let filtered_bytes = sniffer.runtime_data.tot_out_bytes + sniffer.runtime_data.tot_in_bytes;
// let filtered_bytes = sniffer.runtime_data.tot_out_bytes + sniffer.runtime_data.tot_in_bytes;
let all_bytes = sniffer.runtime_data.all_bytes;
let filters = &sniffer.filters;

let dropped_val = if dropped > 0 {
format!(
"{} {}",
dropped,
of_total_translation(language, &get_percentage_string(total, u128::from(dropped)))
)
} else {
none_translation(language).to_string()
};
let bytes_value = if dropped > 0 {
ByteMultiple::formatted_string(filtered_bytes)
} else {
format!(
"{} {}",
&ByteMultiple::formatted_string(filtered_bytes),
of_total_translation(language, &get_percentage_string(all_bytes, filtered_bytes))
)
};
// let filters = &sniffer.filters;
//
// let dropped_val = if dropped > 0 {
// format!(
// "{} {}",
// dropped,
// of_total_translation(language, &get_percentage_string(total, u128::from(dropped)))
// )
// } else {
// none_translation(language).to_string()
// };
// let bytes_value = if dropped > 0 {
// ByteMultiple::formatted_string(filtered_bytes)
// } else {
// format!(
// "{} {}",
// &ByteMultiple::formatted_string(filtered_bytes),
// of_total_translation(language, &get_percentage_string(all_bytes, filtered_bytes))
// )
// };
//
// Column::new()
// .spacing(10)
// .push(get_active_filters_col(
// filters,
// language,
// font,
// font_headers,
// false,
// ))
// .push(TextType::highlighted_subtitle_with_desc(
// filtered_bytes_translation(language),
// &bytes_value,
// font,
// ))
// .push(TextType::highlighted_subtitle_with_desc(
// filtered_packets_translation(language),
// &format!(
// "{} {}",
// filtered,
// of_total_translation(language, &get_percentage_string(total, filtered))
// ),
// font,
// ))
// .push(TextType::highlighted_subtitle_with_desc(
// dropped_packets_translation(language),
// &dropped_val,
// font,
// ))

let radius = 100;
let donuts_row = Row::new()
.padding(Padding::ZERO.top(10))
.spacing(20)
.push(donut_chart(
37.0,
ByteMultiple::formatted_string(all_bytes),
font,
))
.push(donut_chart(37.0, "IPv", font))
.push(donut_chart(37.0, "proto", font));

Column::new()
.width(Length::Fill)
.spacing(10)
.push(get_active_filters_col(
filters,
language,
font,
font_headers,
false,
))
.push(TextType::highlighted_subtitle_with_desc(
filtered_bytes_translation(language),
&bytes_value,
font,
))
.push(TextType::highlighted_subtitle_with_desc(
filtered_packets_translation(language),
&format!(
"{} {}",
filtered,
of_total_translation(language, &get_percentage_string(total, filtered))
),
font,
))
.push(TextType::highlighted_subtitle_with_desc(
dropped_packets_translation(language),
&dropped_val,
font,
))
.align_x(Alignment::Center)
.push(donuts_row)
}

const MIN_BARS_LENGTH: f32 = 10.0;
Expand Down
56 changes: 56 additions & 0 deletions src/gui/styles/donut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use crate::chart::types::donut_chart::Status;
use crate::gui::styles::button::ButtonType;
use crate::gui::styles::types::style_type::StyleType;
use iced::Color;

#[derive(Default)]
pub enum DonutType {
#[default]
Standard,
}

impl DonutType {
fn active(&self, style: &StyleType) -> Style {
let colors = style.get_palette();
let ext = style.get_extension();
Style {
background: Color::TRANSPARENT,
incoming: colors.secondary,
outgoing: colors.outgoing,
rail: colors.primary,
text_color: colors.text_body,
}
}
}

impl Catalog for StyleType {
type Class<'a> = DonutType;

fn default<'a>() -> Self::Class<'a> {
Self::Class::default()
}

fn style(&self, class: &Self::Class<'_>) -> Style {
class.active(self)
}
}

pub struct Style {
pub(crate) background: Color,
pub(crate) incoming: Color,
pub(crate) outgoing: Color,
pub(crate) rail: Color,
pub(crate) text_color: Color,
}

/// The theme catalog of a [`ProgressBar`].
pub trait Catalog: Sized {
/// The item class of the [`Catalog`].
type Class<'a>;

/// The default class produced by the [`Catalog`].
fn default<'a>() -> Self::Class<'a>;

/// The [`Style`] of a class with the given status.
fn style(&self, class: &Self::Class<'_>) -> Style;
}
1 change: 1 addition & 0 deletions src/gui/styles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod checkbox;
pub mod combobox;
pub mod container;
pub mod custom_themes;
pub mod donut;
pub mod picklist;
pub mod radio;
pub mod rule;
Expand Down

0 comments on commit a942932

Please sign in to comment.