Repository: GyulyVGC/sniffnet (language: rust)

Issue:
Avoid dealignment of report when longer addresses are received
Currently, when addresses longer than 25 characters are received, the report row will look like this:

![Screenshot 2023-02-08 at 08 35 38](https://user-images.githubusercontent.com/100347457/217464235-cdbbbc48-eba7-4d48-9092-71bb952b2336.png)

causing a dealignment of its text fields.

I was not able to reproduce this case on my PC and I think it's not a so common case, but it was first documented in issue #48.

I partially solved that issue introducing a horizontal scrollbar, as you can see from the picture, but I'd like to find a better solution for the long term.

The best solution would be to use a table widget, but [iced](https://github.com/iced-rs/iced) doesn't support it yet.




Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf83a6a91..4b5d1bb8e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ All Sniffnet releases with the relative changes are documented in this file.
 - Introduced new filtering capabilities to allow users specify custom values of ports and IP addresses ([#414](https://github.com/GyulyVGC/sniffnet/pull/414))
 - The size of text and widgets can now be customised by setting a proper zoom value (fixes [#202](https://github.com/GyulyVGC/sniffnet/issues/202) and [#344](https://github.com/GyulyVGC/sniffnet/issues/344))
 - Added possibility to totally customize the app's theme via styles defined in TOML files ([#286](https://github.com/GyulyVGC/sniffnet/pull/286) and [#419](https://github.com/GyulyVGC/sniffnet/pull/419))
+- Upgraded inspect page table: multiple new search filters, additional sorting options, and always keep a correct fields alignment ([#442](https://github.com/GyulyVGC/sniffnet/pull/442) — fixes [#63](https://github.com/GyulyVGC/sniffnet/issues/63))
 - Added support for more link types in addition to Ethernet: raw IP packets and null/loopback packets are now correctly parsed ([#421](https://github.com/GyulyVGC/sniffnet/pull/421))
 - IP addresses can now be copied to clipboard from the popup related to a given entry of the connections table, and a new search parameter has been introduced in Inspect page to allow users filter their connections based on IP address values ([#409](https://github.com/GyulyVGC/sniffnet/pull/409))
 - Added Japanese translation 🇯🇵 ([#343](https://github.com/GyulyVGC/sniffnet/pull/343))
@@ -25,6 +26,7 @@ All Sniffnet releases with the relative changes are documented in this file.
 - Fixed bug causing the application's icon not to be visible in some Linux environments
 - Fixed a build failure on `powerpc64` ([#356](https://github.com/GyulyVGC/sniffnet/pull/356) — fixes [#353](https://github.com/GyulyVGC/sniffnet/issues/353))
 - Fixed a typo in Russian translation ([#389](https://github.com/GyulyVGC/sniffnet/pull/389))
+- Fixed icon inconsistency in case of directed broadcast traffic
 
 ## [1.2.2] - 2023-08-08
 - Added option to set different shades of color gradients for each of the available themes
diff --git a/resources/fonts/full/sarasa-mono-sc-bold.ttf b/resources/fonts/full/sarasa-mono-sc-bold.ttf
index 26238878c..7e3b0e172 100644
Binary files a/resources/fonts/full/sarasa-mono-sc-bold.ttf and b/resources/fonts/full/sarasa-mono-sc-bold.ttf differ
diff --git a/resources/fonts/full/sarasa-mono-sc-regular.ttf b/resources/fonts/full/sarasa-mono-sc-regular.ttf
index 9994b9f9c..0a03232b4 100644
Binary files a/resources/fonts/full/sarasa-mono-sc-regular.ttf and b/resources/fonts/full/sarasa-mono-sc-regular.ttf differ
diff --git a/resources/fonts/full/subset_characters.txt b/resources/fonts/full/subset_characters.txt
index 293e2c840..2c07a7083 100644
--- a/resources/fonts/full/subset_characters.txt
+++ b/resources/fonts/full/subset_characters.txt
@@ -2,6 +2,7 @@
 !
 "
 #
+$
 %
 &
 '
@@ -191,6 +192,7 @@ z
 ώ
 І
 А
+Б
 В
 Г
 Д
@@ -287,6 +289,7 @@ z
 ۰
 ۳
 •
+…
 ↗
 ⏎
 ✔
diff --git a/resources/fonts/subset/icons.ttf b/resources/fonts/subset/icons.ttf
index bc7a0b36b..3e65c1e11 100644
Binary files a/resources/fonts/subset/icons.ttf and b/resources/fonts/subset/icons.ttf differ
diff --git a/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf b/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf
index cf9d3aa58..09abdf4f7 100644
Binary files a/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf and b/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf differ
diff --git a/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf b/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf
index 92aef91b1..4630bc5a3 100644
Binary files a/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf and b/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf differ
diff --git a/src/chart/types/traffic_chart.rs b/src/chart/types/traffic_chart.rs
index 8808f237f..4806fb183 100644
--- a/src/chart/types/traffic_chart.rs
+++ b/src/chart/types/traffic_chart.rs
@@ -8,6 +8,7 @@ use iced::{Element, Renderer};
 use plotters::prelude::*;
 use plotters_iced::{Chart, ChartBuilder, ChartWidget, DrawingBackend};
 
+use crate::gui::app::FONT_FAMILY_NAME;
 use crate::gui::styles::style_constants::CHARTS_LINE_BORDER;
 use crate::gui::styles::types::palette::to_rgb_color;
 use crate::gui::types::message::Message;
@@ -136,7 +137,7 @@ impl Chart<Message> for TrafficChart {
             .y_max_light_lines(1)
             .y_labels(7)
             .label_style(
-                ("Sarasa Mono SC", 12)
+                (FONT_FAMILY_NAME, 12.5)
                     .into_font()
                     .style(font_weight)
                     .color(&color_font),
@@ -144,7 +145,7 @@ impl Chart<Message> for TrafficChart {
             .y_label_formatter(if self.chart_type.eq(&ChartType::Packets) {
                 &|packets| packets.abs().to_string()
             } else {
-                &|bytes| get_formatted_bytes_string_with_b(u128::from(bytes.unsigned_abs()))
+                &|bytes| get_formatted_bytes_string_with_b(u128::from(bytes.unsigned_abs()), 0)
             })
             .draw()
             .unwrap();
@@ -196,7 +197,7 @@ impl Chart<Message> for TrafficChart {
             .background_style(buttons_color.mix(0.6))
             .border_style(buttons_color.stroke_width(CHARTS_LINE_BORDER * 2))
             .label_font(
-                ("Sarasa Mono SC", 13.5)
+                (FONT_FAMILY_NAME, 13.5)
                     .into_font()
                     .style(font_weight)
                     .color(&color_font),
diff --git a/src/countries/country_utils.rs b/src/countries/country_utils.rs
index 49e8990c7..ae99ee636 100644
--- a/src/countries/country_utils.rs
+++ b/src/countries/country_utils.rs
@@ -290,10 +290,6 @@ fn get_flag_from_country(
                 tooltip = your_network_adapter_translation(language);
                 svg_style = SvgType::AdaptColor;
                 COMPUTER
-            } else if is_local {
-                tooltip = local_translation(language);
-                svg_style = SvgType::AdaptColor;
-                HOME
             } else if traffic_type.eq(&TrafficType::Multicast) {
                 tooltip = "Multicast".to_string();
                 svg_style = SvgType::AdaptColor;
@@ -302,6 +298,10 @@ fn get_flag_from_country(
                 tooltip = "Broadcast".to_string();
                 svg_style = SvgType::AdaptColor;
                 BROADCAST
+            } else if is_local {
+                tooltip = local_translation(language);
+                svg_style = SvgType::AdaptColor;
+                HOME
             } else {
                 tooltip = unknown_translation(language);
                 svg_style = SvgType::AdaptColor;
@@ -357,9 +357,9 @@ pub fn get_computer_tooltip(
     let content = Svg::new(Handle::from_memory(Vec::from(
         match (is_my_address, is_local, traffic_type) {
             (true, _, _) => COMPUTER,
+            (false, _, TrafficType::Multicast) => MULTICAST,
+            (false, _, TrafficType::Broadcast) => BROADCAST,
             (false, true, _) => HOME,
-            (false, false, TrafficType::Multicast) => MULTICAST,
-            (false, false, TrafficType::Broadcast) => BROADCAST,
             (false, false, TrafficType::Unicast) => UNKNOWN,
         },
     )))
@@ -369,9 +369,9 @@ pub fn get_computer_tooltip(
 
     let tooltip = match (is_my_address, is_local, traffic_type) {
         (true, _, _) => your_network_adapter_translation(language),
+        (false, _, TrafficType::Multicast) => "Multicast".to_string(),
+        (false, _, TrafficType::Broadcast) => "Broadcast".to_string(),
         (false, true, _) => local_translation(language),
-        (false, false, TrafficType::Multicast) => "Multicast".to_string(),
-        (false, false, TrafficType::Broadcast) => "Broadcast".to_string(),
         (false, false, TrafficType::Unicast) => unknown_translation(language),
     };
 
diff --git a/src/gui/app.rs b/src/gui/app.rs
index ad03540fc..dd672c607 100644
--- a/src/gui/app.rs
+++ b/src/gui/app.rs
@@ -33,6 +33,9 @@ use crate::{ConfigSettings, StyleType, SNIFFNET_TITLECASE};
 /// Update period (milliseconds)
 pub const PERIOD_TICK: u64 = 1000;
 
+pub const FONT_FAMILY_NAME: &str = "Sarasa Mono SC for Sniffnet";
+pub const ICON_FONT_FAMILY_NAME: &str = "Icons for Sniffnet";
+
 impl Application for Sniffer {
     type Executor = executor::Default;
     type Message = Message;
diff --git a/src/gui/components/footer.rs b/src/gui/components/footer.rs
index 260a923d1..ade1ee71b 100644
--- a/src/gui/components/footer.rs
+++ b/src/gui/components/footer.rs
@@ -93,6 +93,7 @@ fn get_button_github(font: Font) -> Tooltip<'static, Message, Renderer<StyleType
 fn get_button_sponsor(font: Font) -> Tooltip<'static, Message, Renderer<StyleType>> {
     let content = button(
         Text::new('❤'.to_string())
+            .font(font)
             .size(23)
             .style(TextType::Sponsor)
             .horizontal_alignment(Horizontal::Center)
diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs
index 0d9d9e7e6..01dfad584 100644
--- a/src/gui/pages/connection_details_page.rs
+++ b/src/gui/pages/connection_details_page.rs
@@ -29,7 +29,6 @@ use crate::networking::types::traffic_direction::TrafficDirection;
 use crate::translations::translations::{
     address_translation, application_protocol_translation, incoming_translation,
     outgoing_translation, packets_translation, protocol_translation,
-    transport_protocol_translation,
 };
 use crate::translations::translations_2::{
     administrative_entity_translation, connection_details_translation, destination_translation,
@@ -189,11 +188,6 @@ fn col_info(
     language: Language,
 ) -> Column<'static, Message, Renderer<StyleType>> {
     let is_icmp = key.protocol.eq(&Protocol::ICMP);
-    let protocol_caption = if is_icmp {
-        protocol_translation(language)
-    } else {
-        transport_protocol_translation(language)
-    };
 
     let mut ret_val = Column::new()
         .spacing(10)
@@ -211,7 +205,7 @@ fn col_info(
             ),
         )
         .push(TextType::highlighted_subtitle_with_desc(
-            protocol_caption,
+            protocol_translation(language),
             &key.protocol.to_string(),
             font,
         ));
@@ -236,7 +230,7 @@ fn col_info(
         ),
         &format!(
             "{}\n   {} {}",
-            get_formatted_bytes_string_with_b(val.transmitted_bytes),
+            get_formatted_bytes_string_with_b(val.transmitted_bytes, 1),
             val.transmitted_packets,
             packets_translation(language)
         ),
diff --git a/src/gui/pages/initial_page.rs b/src/gui/pages/initial_page.rs
index e2522cff5..b5da0960a 100644
--- a/src/gui/pages/initial_page.rs
+++ b/src/gui/pages/initial_page.rs
@@ -6,7 +6,6 @@ use std::collections::HashSet;
 
 use iced::alignment::{Horizontal, Vertical};
 use iced::widget::scrollable::Direction;
-use iced::widget::text::Shaping;
 use iced::widget::tooltip::Position;
 use iced::widget::{
     button, horizontal_space, vertical_space, Button, Column, Container, Row, Rule, Scrollable,
@@ -119,7 +118,6 @@ fn col_ip_buttons(
         buttons_row = buttons_row.push(
             Button::new(
                 Text::new(format!("{option} {check_symbol}"))
-                    .shaping(Shaping::Advanced)
                     .horizontal_alignment(Horizontal::Center)
                     .vertical_alignment(Vertical::Center)
                     .font(font),
@@ -160,7 +158,6 @@ fn col_protocol_buttons(
             Button::new(
                 Text::new(format!("{option} {check_symbol}"))
                     .width(Length::Fill)
-                    .shaping(Shaping::Advanced)
                     .horizontal_alignment(Horizontal::Center)
                     .vertical_alignment(Vertical::Center)
                     .font(font),
diff --git a/src/gui/pages/inspect_page.rs b/src/gui/pages/inspect_page.rs
index b791cf373..f05c9381a 100644
--- a/src/gui/pages/inspect_page.rs
+++ b/src/gui/pages/inspect_page.rs
@@ -1,9 +1,11 @@
+use std::cmp::min;
+
 use iced::alignment::{Horizontal, Vertical};
 use iced::widget::scrollable::Direction;
-use iced::widget::{button, horizontal_space, vertical_space, Rule};
-use iced::widget::{
-    lazy, Button, Checkbox, Column, Container, PickList, Row, Scrollable, Text, TextInput,
-};
+use iced::widget::text_input::Side;
+use iced::widget::tooltip::Position;
+use iced::widget::{button, horizontal_space, text_input, vertical_space, Rule, Toggler, Tooltip};
+use iced::widget::{lazy, Button, Column, Container, Row, Scrollable, Text, TextInput};
 use iced::{alignment, Alignment, Font, Length, Renderer};
 
 use crate::gui::components::tab::get_pages_tabs;
@@ -11,19 +13,21 @@ use crate::gui::components::types::my_modal::MyModal;
 use crate::gui::styles::button::ButtonType;
 use crate::gui::styles::container::ContainerType;
 use crate::gui::styles::scrollbar::ScrollbarType;
-use crate::gui::styles::style_constants::FONT_SIZE_TITLE;
+use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, FONT_SIZE_SUBTITLE, ICONS};
 use crate::gui::styles::text::TextType;
 use crate::gui::styles::text_input::TextInputType;
 use crate::gui::types::message::Message;
+use crate::networking::types::address_port_pair::AddressPortPair;
+use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
 use crate::networking::types::search_parameters::{FilterInputType, SearchParameters};
 use crate::networking::types::traffic_direction::TrafficDirection;
 use crate::report::get_report_entries::get_searched_entries;
-use crate::translations::translations::{address_translation, application_protocol_translation};
+use crate::report::types::report_col::ReportCol;
 use crate::translations::translations_2::{
     administrative_entity_translation, country_translation, domain_name_translation,
-    no_search_results_translation, only_show_favorites_translation, search_filters_translation,
-    showing_results_translation, sort_by_translation,
+    no_search_results_translation, only_show_favorites_translation, showing_results_translation,
 };
+use crate::translations::translations_3::filter_by_host_translation;
 use crate::utils::types::icon::Icon;
 use crate::{ConfigSettings, Language, ReportSortType, RunningPage, Sniffer, StyleType};
 
@@ -53,24 +57,6 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container<Message, Renderer<StyleType>
 
     tab_and_body = tab_and_body.push(tabs);
 
-    let sort_active_str = sniffer.report_sort_type.get_picklist_label(language);
-    let sort_list_str: Vec<&str> = ReportSortType::all_strings(language);
-    let picklist_sort = PickList::new(
-        sort_list_str.clone(),
-        Some(sort_active_str),
-        move |selected_str| {
-            if selected_str == *sort_list_str.first().unwrap_or(&"") {
-                Message::ReportSortSelection(ReportSortType::MostRecent)
-            } else if selected_str == *sort_list_str.get(1).unwrap_or(&"") {
-                Message::ReportSortSelection(ReportSortType::MostBytes)
-            } else {
-                Message::ReportSortSelection(ReportSortType::MostPackets)
-            }
-        },
-    )
-    .padding([2, 7])
-    .font(font);
-
     let report = lazy(
         (
             sniffer.runtime_data.tot_sent_packets + sniffer.runtime_data.tot_received_packets,
@@ -83,34 +69,39 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container<Message, Renderer<StyleType>
         move |_| lazy_report(sniffer),
     );
 
+    let col_report = Column::new()
+        .height(Length::Fill)
+        .width(Length::Fill)
+        .align_items(Alignment::Start)
+        .push(report_header_row(
+            language,
+            &sniffer.search,
+            font,
+            sniffer.report_sort_type,
+        ))
+        .push(vertical_space(4))
+        .push(Rule::horizontal(5))
+        .push(report);
+
     body = body
         .push(
-            Container::new(
-                Row::new()
-                    .push(filters_col(&sniffer.search, font, language))
-                    .push(Rule::vertical(25))
-                    .push(
-                        Column::new()
-                            .spacing(10)
-                            .push(
-                                Text::new(sort_by_translation(language))
-                                    .font(font)
-                                    .style(TextType::Title)
-                                    .size(FONT_SIZE_TITLE),
-                            )
-                            .push(picklist_sort),
-                    ),
-            )
-            .height(Length::Fixed(165.0))
-            .padding(10)
-            .style(ContainerType::BorderedRound),
+            Container::new(host_filters_col(&sniffer.search, font, language))
+                .padding(10)
+                .style(ContainerType::BorderedRound),
         )
-        .push(report);
+        .push(
+            Container::new(col_report)
+                .align_y(Vertical::Center)
+                .align_x(Horizontal::Center)
+                .padding([10, 7, 3, 7])
+                .width(Length::Fixed(1042.0))
+                .style(ContainerType::BorderedRound),
+        );
 
     Container::new(Column::new().push(tab_and_body.push(body))).height(Length::Fill)
 }
 
-fn lazy_report(sniffer: &Sniffer) -> Container<'static, Message, Renderer<StyleType>> {
+fn lazy_report(sniffer: &Sniffer) -> Column<'static, Message, Renderer<StyleType>> {
     let ConfigSettings {
         style, language, ..
     } = sniffer.configs.lock().unwrap().settings;
@@ -118,55 +109,33 @@ fn lazy_report(sniffer: &Sniffer) -> Container<'static, Message, Renderer<StyleT
 
     let (search_results, results_number) = get_searched_entries(sniffer);
 
-    let mut col_report = Column::new()
+    let mut ret_val = Column::new()
         .height(Length::Fill)
         .width(Length::Fill)
-        .align_items(Alignment::Center);
+        .align_items(Alignment::Start);
 
-    let mut scroll_report = Column::new();
+    let mut scroll_report = Column::new().align_items(Alignment::Start);
     let start_entry_num = (sniffer.page_number - 1) * 20 + 1;
     let end_entry_num = start_entry_num + search_results.len() - 1;
     for report_entry in search_results {
-        let entry_text_type = if report_entry.val.traffic_direction == TrafficDirection::Outgoing {
-            TextType::Outgoing
-        } else {
-            TextType::Incoming
-        };
-        let entry_row = Row::new()
-            .align_items(Alignment::Center)
-            .push(
-                Text::new(format!("  {}{}  ", report_entry.key, report_entry.val))
-                    .style(entry_text_type)
-                    .font(font),
-            )
-            .push(report_entry.tooltip)
-            .push(Text::new("  "));
-
         scroll_report = scroll_report.push(
-            button(entry_row)
+            button(row_report_entry(&report_entry.0, &report_entry.1, font))
                 .padding(2)
                 .on_press(Message::ShowModal(MyModal::ConnectionDetails(
-                    report_entry.key,
+                    report_entry.0,
                 )))
                 .style(ButtonType::Neutral),
         );
     }
     if results_number > 0 {
-        col_report = col_report
-            .push(Text::new("      Src IP address       Src port      Dst IP address       Dst port  Layer4   Layer7     Packets     Bytes   Country").vertical_alignment(Vertical::Center).height(Length::FillPortion(2)).font(font))
-            .push(Rule::horizontal(5))
+        ret_val = ret_val
             .push(
                 Scrollable::new(scroll_report)
-                    .height(Length::FillPortion(15))
+                    .height(Length::Fill)
                     .width(Length::Fill)
-                    .direction(Direction::Both {
-                        vertical: ScrollbarType::properties(),
-                        horizontal: ScrollbarType::properties(),
-                    })
-            )
-            .push(
-                Rule::horizontal(5)
+                    .direction(Direction::Vertical(ScrollbarType::properties())),
             )
+            .push(Rule::horizontal(5))
             .push(get_change_page_row(
                 font,
                 language,
@@ -176,7 +145,7 @@ fn lazy_report(sniffer: &Sniffer) -> Container<'static, Message, Renderer<StyleT
                 results_number,
             ));
     } else {
-        col_report = col_report.push(
+        ret_val = ret_val.push(
             Column::new()
                 .width(Length::Fill)
                 .height(Length::Fill)
@@ -190,15 +159,148 @@ fn lazy_report(sniffer: &Sniffer) -> Container<'static, Message, Renderer<StyleT
         );
     }
 
-    Container::new(col_report)
-        .align_y(Vertical::Center)
-        .align_x(Horizontal::Center)
-        .padding([10, 7, 7, 7])
-        .width(Length::Fixed(1042.0))
-        .style(ContainerType::BorderedRound)
+    ret_val
 }
 
-fn filters_col(
+fn report_header_row(
+    language: Language,
+    search_params: &SearchParameters,
+    font: Font,
+    sort_type: ReportSortType,
+) -> Row<'static, Message, Renderer<StyleType>> {
+    let mut ret_val = Row::new().padding([0, 2]).align_items(Alignment::Center);
+    for report_col in ReportCol::ALL {
+        let (title_display, title_small_display, tooltip_val) =
+            title_report_col_display(&report_col, language);
+        let title_row = Row::new()
+            .align_items(Alignment::End)
+            .push(Text::new(title_display).font(font))
+            .push(
+                Text::new(title_small_display)
+                    .font(font)
+                    .size(FONT_SIZE_FOOTER),
+            );
+        let tooltip_style = if tooltip_val.is_empty() {
+            ContainerType::Neutral
+        } else {
+            ContainerType::Tooltip
+        };
+        let title_tooltip = Tooltip::new(title_row, tooltip_val, Position::FollowCursor)
+            .font(font)
+            .style(tooltip_style);
+
+        let mut col_header = Column::new()
+            .align_items(Alignment::Center)
+            .width(Length::Fixed(report_col.get_width()))
+            .height(Length::Fixed(56.0))
+            .push(title_tooltip);
+        if report_col != ReportCol::Packets && report_col != ReportCol::Bytes {
+            col_header = col_header.push(
+                Container::new(filter_input(
+                    report_col.get_filter_input_type(),
+                    search_params.clone(),
+                    font,
+                ))
+                .height(Length::Fill)
+                .align_y(Vertical::Center),
+            );
+        } else {
+            col_header = col_header.push(sort_arrows(sort_type, &report_col));
+        }
+        ret_val = ret_val.push(col_header);
+    }
+    ret_val
+}
+
+fn title_report_col_display(
+    report_col: &ReportCol,
+    language: Language,
+) -> (String, String, String) {
+    let max_chars = report_col.get_max_chars(Some(language));
+    let title = report_col.get_title(language);
+    let title_direction_info = report_col.get_title_direction_info(language);
+    let chars_title = title.chars().collect::<Vec<char>>();
+    let chars_title_direction_info = title_direction_info.chars().collect::<Vec<char>>();
+    if chars_title.len() + chars_title_direction_info.len() <= max_chars {
+        (title, title_direction_info, String::new())
+    } else if chars_title.len() >= max_chars - 4 {
+        (
+            chars_title[..min(max_chars - 2, chars_title.len())]
+                .iter()
+                .collect::<String>(),
+            String::from("…"),
+            [title, title_direction_info].concat(),
+        )
+    } else {
+        // title length is < max_chars - 4, but with direction info the whole thing is too long
+        (
+            title.clone(),
+            [
+                &chars_title_direction_info[..max_chars - chars_title.len() - 2]
+                    .iter()
+                    .collect::<String>(),
+                "…",
+            ]
+            .concat(),
+            [title, title_direction_info].concat(),
+        )
+    }
+}
+
+fn sort_arrows(
+    active_sort_type: ReportSortType,
+    report_col: &ReportCol,
+) -> Container<'static, Message, Renderer<StyleType>> {
+    Container::new(
+        button(
+            active_sort_type
+                .icon(report_col)
+                .horizontal_alignment(Horizontal::Center)
+                .vertical_alignment(Vertical::Center),
+        )
+        .style(active_sort_type.button_type(report_col))
+        .on_press(Message::ReportSortSelection(
+            active_sort_type.next_sort(report_col),
+        )),
+    )
+    .align_y(Vertical::Center)
+    .height(Length::Fill)
+}
+
+fn row_report_entry(
+    key: &AddressPortPair,
+    val: &InfoAddressPortPair,
+    font: Font,
+) -> Row<'static, Message, Renderer<StyleType>> {
+    let text_type = if val.traffic_direction == TrafficDirection::Outgoing {
+        TextType::Outgoing
+    } else {
+        TextType::Incoming
+    };
+
+    let mut ret_val = Row::new().align_items(Alignment::Center);
+
+    for report_col in ReportCol::ALL {
+        let max_chars = report_col.get_max_chars(None);
+        let col_value = report_col.get_value(key, val);
+        ret_val = ret_val.push(
+            Container::new(
+                Text::new(if col_value.len() <= max_chars {
+                    col_value
+                } else {
+                    [&col_value[..max_chars - 2], "…"].concat()
+                })
+                .font(font)
+                .style(text_type),
+            )
+            .align_x(Horizontal::Center)
+            .width(Length::Fixed(report_col.get_width())),
+        );
+    }
+    ret_val
+}
+
+fn host_filters_col(
     search_params: &SearchParameters,
     font: Font,
     language: Language,
@@ -206,22 +308,50 @@ fn filters_col(
     let search_params2 = search_params.clone();
 
     let mut title_row = Row::new().spacing(10).align_items(Alignment::Center).push(
-        Text::new(search_filters_translation(language))
+        Text::new(filter_by_host_translation(language))
             .font(font)
-            .style(TextType::Title)
-            .size(FONT_SIZE_TITLE),
+            .style(TextType::Subtitle)
+            .size(FONT_SIZE_SUBTITLE),
     );
-    if search_params.is_some_filter_active() {
-        title_row = title_row.push(button_clear_filter(SearchParameters::default(), font));
+    if search_params.is_some_host_filter_active() {
+        title_row = title_row.push(button_clear_filter(
+            search_params.reset_host_filters(),
+            font,
+        ));
     }
 
-    Column::new()
-        .push(title_row)
-        .push(vertical_space(Length::Fixed(10.0)))
+    let input_country = filter_input(FilterInputType::Country, search_params.clone(), font)
+        .width(Length::Fixed(95.0));
+    let input_domain = filter_input(FilterInputType::Domain, search_params.clone(), font)
+        .width(Length::Fixed(190.0));
+    let input_as_name = filter_input(FilterInputType::AsName, search_params.clone(), font)
+        .width(Length::Fixed(190.0));
+
+    let container_country = Row::new()
+        .spacing(5)
+        .align_items(Alignment::Center)
+        .push(Text::new(format!("{}:", country_translation(language))).font(font))
+        .push(input_country);
+
+    let container_domain = Row::new()
+        .spacing(5)
+        .align_items(Alignment::Center)
+        .push(Text::new(format!("{}:", domain_name_translation(language))).font(font))
+        .push(input_domain);
+
+    let container_as_name = Row::new()
+        .spacing(5)
+        .align_items(Alignment::Center)
+        .push(Text::new(format!("{}:", administrative_entity_translation(language))).font(font))
+        .push(input_as_name);
+
+    let col1 = Column::new()
+        .align_items(Alignment::Start)
+        .spacing(5)
         .push(
             Container::new(
-                Checkbox::new(
-                    only_show_favorites_translation(language),
+                Toggler::new(
+                    only_show_favorites_translation(language).to_owned(),
                     search_params.only_favorites,
                     move |toggled| {
                         Message::Search(SearchParameters {
@@ -230,143 +360,71 @@ fn filters_col(
                         })
                     },
                 )
+                .width(Length::Shrink)
                 .spacing(5)
-                .size(18)
+                .size(23)
                 .font(font),
             )
-            .padding([5, 8])
-            .style(if search_params.only_favorites {
-                ContainerType::Badge
-            } else {
-                ContainerType::Neutral
-            }),
-        )
-        .push(
-            Row::new()
-                .align_items(Alignment::Center)
-                .spacing(10)
-                .push(filter_input(
-                    FilterInputType::Address,
-                    &search_params.address,
-                    address_translation(language),
-                    120.0,
-                    search_params.clone(),
-                    font,
-                ))
-                .push(filter_input(
-                    FilterInputType::App,
-                    &search_params.app,
-                    application_protocol_translation(language),
-                    60.0,
-                    search_params.clone(),
-                    font,
-                ))
-                .push(filter_input(
-                    FilterInputType::Country,
-                    &search_params.country,
-                    country_translation(language),
-                    30.0,
-                    search_params.clone(),
-                    font,
-                )),
+            .padding([5, 0]),
         )
+        .push(container_domain);
+
+    let col2 = Column::new()
+        .align_items(Alignment::Start)
+        .spacing(5)
+        .push(container_country)
+        .push(container_as_name);
+
+    Column::new()
+        .align_items(Alignment::Start)
+        .push(title_row)
+        .push(vertical_space(10))
         .push(
             Row::new()
                 .align_items(Alignment::Center)
-                .spacing(10)
-                .push(filter_input(
-                    FilterInputType::Domain,
-                    &search_params.domain,
-                    domain_name_translation(language),
-                    120.0,
-                    search_params.clone(),
-                    font,
-                ))
-                .push(filter_input(
-                    FilterInputType::AS,
-                    &search_params.as_name.clone(),
-                    administrative_entity_translation(language),
-                    120.0,
-                    search_params.clone(),
-                    font,
-                )),
+                .spacing(30)
+                .push(col1)
+                .push(col2),
         )
 }
 
 fn filter_input(
     filter_input_type: FilterInputType,
-    filter_value: &str,
-    caption: &str,
-    width: f32,
     search_params: SearchParameters,
     font: Font,
 ) -> Container<'static, Message, Renderer<StyleType>> {
+    let filter_value = filter_input_type.current_value(&search_params);
     let is_filter_active = !filter_value.is_empty();
 
-    let button_clear = button_clear_filter(
-        match filter_input_type {
-            FilterInputType::App => SearchParameters {
-                app: String::new(),
-                ..search_params.clone()
-            },
-            FilterInputType::Domain => SearchParameters {
-                domain: String::new(),
-                ..search_params.clone()
-            },
-            FilterInputType::Country => SearchParameters {
-                country: String::new(),
-                ..search_params.clone()
-            },
-            FilterInputType::AS => SearchParameters {
-                as_name: String::new(),
-                ..search_params.clone()
-            },
-            FilterInputType::Address => SearchParameters {
-                address: String::new(),
-                ..search_params.clone()
-            },
-        },
-        font,
-    );
+    let button_clear = button_clear_filter(filter_input_type.clear_search(&search_params), font);
 
-    let input = TextInput::new("-", filter_value)
+    let mut input = TextInput::new("", filter_value)
         .on_input(move |new_value| {
-            Message::Search(match filter_input_type {
-                FilterInputType::App => SearchParameters {
-                    app: new_value.trim().to_string(),
-                    ..search_params.clone()
-                },
-                FilterInputType::Domain => SearchParameters {
-                    domain: new_value.trim().to_string(),
-                    ..search_params.clone()
-                },
-                FilterInputType::Country => SearchParameters {
-                    country: new_value.trim().to_string(),
-                    ..search_params.clone()
-                },
-                FilterInputType::AS => SearchParameters {
-                    as_name: new_value.trim().to_string(),
-                    ..search_params.clone()
-                },
-                FilterInputType::Address => SearchParameters {
-                    address: new_value.trim().to_string(),
-                    ..search_params.clone()
-                },
-            })
+            Message::Search(filter_input_type.new_search(&search_params, new_value))
         })
-        .padding([3, 5])
+        .padding([2, 5])
+        .size(FONT_SIZE_FOOTER)
         .font(font)
-        .width(Length::Fixed(width))
+        .width(Length::Fill)
         .style(if is_filter_active {
             TextInputType::Badge
         } else {
             TextInputType::Standard
         });
 
+    if !is_filter_active {
+        input = input.icon(text_input::Icon {
+            font: ICONS,
+            code_point: Icon::Funnel.codepoint(),
+            size: Some(12.0),
+            spacing: 2.0,
+            side: Side::Left,
+        });
+    }
+
     let mut content = Row::new()
         .spacing(5)
         .align_items(Alignment::Center)
-        .push(Text::new(format!("{caption}:")).font(font))
         .push(input);
 
     if is_filter_active {
@@ -374,7 +432,11 @@ fn filter_input(
     }
 
     Container::new(content)
-        .padding(5)
+        .padding(if is_filter_active {
+            [5, 5, 5, 10]
+        } else {
+            [5, 3, 5, 3]
+        })
         .style(if is_filter_active {
             ContainerType::Badge
         } else {
@@ -409,9 +471,10 @@ fn get_change_page_row(
     results_number: usize,
 ) -> Row<'static, Message, Renderer<StyleType>> {
     Row::new()
-        .height(Length::FillPortion(2))
+        .height(Length::Fixed(40.0))
         .align_items(Alignment::Center)
         .spacing(10)
+        .push(horizontal_space(Length::Fill))
         .push(if page_number > 1 {
             Container::new(get_button_change_page(false).width(25.0))
         } else {
@@ -431,6 +494,7 @@ fn get_change_page_row(
         } else {
             Container::new(horizontal_space(25.0))
         })
+        .push(horizontal_space(Length::Fill))
 }
 
 fn button_clear_filter(
@@ -449,3 +513,81 @@ fn button_clear_filter(
     .width(Length::Fixed(20.0))
     .on_press(Message::Search(new_search_parameters))
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::gui::pages::inspect_page::title_report_col_display;
+    use crate::report::types::report_col::ReportCol;
+    use crate::translations::types::language::Language;
+
+    #[test]
+    fn test_table_titles_display_and_tooltip_values_for_each_language() {
+        // check glyph len when adding new language...
+        assert_eq!(Language::ALL.len(), 18);
+        for report_col in ReportCol::ALL {
+            for language in Language::ALL {
+                let (title, title_small, tooltip_val) =
+                    title_report_col_display(&report_col, language);
+                let title_chars = title.chars().collect::<Vec<char>>();
+                let title_small_chars = title_small.chars().collect::<Vec<char>>();
+                let max_chars = report_col.get_max_chars(Some(language));
+                if tooltip_val.is_empty() {
+                    // all is entirely displayed
+                    assert!(title_chars.len() + title_small_chars.len() <= max_chars);
+                    assert_eq!(title, report_col.get_title(language));
+                    assert_eq!(title_small, report_col.get_title_direction_info(language));
+                } else {
+                    // tooltip is the full concatenation
+                    assert_eq!(
+                        tooltip_val,
+                        [
+                            report_col.get_title(language),
+                            report_col.get_title_direction_info(language)
+                        ]
+                        .concat()
+                    );
+                    // displayed values have max len -1 (they include "…" that counts for 2 units)
+                    assert_eq!(title_chars.len() + title_small_chars.len(), max_chars - 1);
+                    if title != report_col.get_title(language) {
+                        // first title part is not full, so second one is suspensions
+                        assert_eq!(title_small, "…");
+                        // check len wrt max
+                        assert!(title_chars.len() >= max_chars - 4);
+                        // first title part is max - 2 chars of full self
+                        assert_eq!(
+                            title,
+                            report_col
+                                .get_title(language)
+                                .chars()
+                                .collect::<Vec<char>>()[..max_chars - 2]
+                                .iter()
+                                .collect::<String>()
+                        );
+                    } else {
+                        // first part is untouched
+                        // second part has correct len
+                        assert_eq!(title_small_chars.len(), max_chars - title_chars.len() - 1);
+                        // second title part is max - title.len - 2 chars of full self, plus suspensions
+                        assert_eq!(
+                            title_small,
+                            [
+                                &report_col
+                                    .get_title_direction_info(language)
+                                    .chars()
+                                    .collect::<Vec<char>>()[..max_chars - 2 - title_chars.len()]
+                                    .iter()
+                                    .collect::<String>(),
+                                "…"
+                            ]
+                            .concat()
+                        );
+                        // second part never terminates with "(…"
+                        assert!(!title_small.ends_with("(…"));
+                        // second part never terminates with " …"
+                        assert!(!title_small.ends_with(" …"));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/gui/pages/notifications_page.rs b/src/gui/pages/notifications_page.rs
index 74a5963e2..056441464 100644
--- a/src/gui/pages/notifications_page.rs
+++ b/src/gui/pages/notifications_page.rs
@@ -235,21 +235,24 @@ fn bytes_notification_log(
     threshold_str.push_str(": ");
     threshold_str.push_str(&get_formatted_bytes_string_with_b(
         (logged_notification.threshold).into(),
+        1,
     ));
 
     threshold_str.push_str(&format!(" {}", per_second_translation(language)));
     let mut incoming_str = " - ".to_string();
     incoming_str.push_str(incoming_translation(language));
     incoming_str.push_str(": ");
-    incoming_str.push_str(&get_formatted_bytes_string_with_b(u128::from(
-        logged_notification.incoming,
-    )));
+    incoming_str.push_str(&get_formatted_bytes_string_with_b(
+        u128::from(logged_notification.incoming),
+        1,
+    ));
     let mut outgoing_str = " - ".to_string();
     outgoing_str.push_str(outgoing_translation(language));
     outgoing_str.push_str(": ");
-    outgoing_str.push_str(&get_formatted_bytes_string_with_b(u128::from(
-        logged_notification.outgoing,
-    )));
+    outgoing_str.push_str(&get_formatted_bytes_string_with_b(
+        u128::from(logged_notification.outgoing),
+        1,
+    ));
     let content = Row::new()
         .spacing(30)
         .align_items(Alignment::Center)
@@ -291,9 +294,10 @@ fn bytes_notification_log(
                 .push(
                     Text::new(bytes_exceeded_value_translation(
                         language,
-                        &get_formatted_bytes_string_with_b(u128::from(
-                            logged_notification.incoming + logged_notification.outgoing,
-                        )),
+                        &get_formatted_bytes_string_with_b(
+                            u128::from(logged_notification.incoming + logged_notification.outgoing),
+                            1,
+                        ),
                     ))
                     .font(font),
                 )
diff --git a/src/gui/pages/overview_page.rs b/src/gui/pages/overview_page.rs
index b0d4a7317..e1064c51c 100644
--- a/src/gui/pages/overview_page.rs
+++ b/src/gui/pages/overview_page.rs
@@ -15,6 +15,7 @@ use iced::{Alignment, Font, Length, Renderer};
 
 use crate::countries::country_utils::get_flag_tooltip;
 use crate::countries::flags_pictures::FLAGS_WIDTH_BIG;
+use crate::countries::types::country::Country;
 use crate::gui::components::tab::get_pages_tabs;
 use crate::gui::styles::button::ButtonType;
 use crate::gui::styles::container::ContainerType;
@@ -305,7 +306,10 @@ fn col_host(width: f32, sniffer: &Sniffer) -> Column<'static, Message, Renderer<
                         Text::new(if chart_type.eq(&ChartType::Packets) {
                             data_info_host.data_info.tot_packets().to_string()
                         } else {
-                            get_formatted_bytes_string_with_b(data_info_host.data_info.tot_bytes())
+                            get_formatted_bytes_string_with_b(
+                                data_info_host.data_info.tot_bytes(),
+                                1,
+                            )
                         })
                         .font(font),
                     ),
@@ -330,8 +334,12 @@ fn col_host(width: f32, sniffer: &Sniffer) -> Column<'static, Message, Renderer<
                 .padding([5, 15, 5, 10])
                 .on_press(Message::Search(SearchParameters {
                     domain: host.domain.clone(),
-                    country: host.country.to_string().clone(),
                     as_name: host.asn.name.clone(),
+                    country: if host.country == Country::ZZ {
+                        String::new()
+                    } else {
+                        host.country.to_string()
+                    },
                     ..SearchParameters::default()
                 }))
                 .style(ButtonType::Neutral),
@@ -407,7 +415,7 @@ fn col_app(width: f32, sniffer: &Sniffer) -> Column<'static, Message, Renderer<S
                         Text::new(if chart_type.eq(&ChartType::Packets) {
                             data_info.tot_packets().to_string()
                         } else {
-                            get_formatted_bytes_string_with_b(data_info.tot_bytes())
+                            get_formatted_bytes_string_with_b(data_info.tot_bytes(), 1)
                         })
                         .font(font),
                     ),
@@ -418,7 +426,7 @@ fn col_app(width: f32, sniffer: &Sniffer) -> Column<'static, Message, Renderer<S
             button(content)
                 .padding([5, 15, 8, 10])
                 .on_press(Message::Search(SearchParameters {
-                    app: app.to_string(),
+                    app_proto: app.to_string(),
                     ..SearchParameters::default()
                 }))
                 .style(ButtonType::Neutral),
@@ -608,11 +616,11 @@ fn col_bytes_packets(
         none_translation(language).to_string()
     };
     let bytes_value = if dropped > 0 {
-        get_formatted_bytes_string_with_b(filtered_bytes)
+        get_formatted_bytes_string_with_b(filtered_bytes, 1)
     } else {
         format!(
             "{} {}",
-            &get_formatted_bytes_string_with_b(filtered_bytes),
+            &get_formatted_bytes_string_with_b(filtered_bytes, 1),
             of_total_translation(language, &get_percentage_string(all_bytes, filtered_bytes))
         )
     };
diff --git a/src/gui/pages/settings_general_page.rs b/src/gui/pages/settings_general_page.rs
index ac228af82..b0fed3057 100644
--- a/src/gui/pages/settings_general_page.rs
+++ b/src/gui/pages/settings_general_page.rs
@@ -1,10 +1,9 @@
 use std::sync::Arc;
 
-use iced::advanced::widget::Text;
 use iced::alignment::{Horizontal, Vertical};
 use iced::widget::tooltip::Position;
 use iced::widget::{
-    button, horizontal_space, vertical_space, Column, Container, PickList, Row, Rule, Slider,
+    button, horizontal_space, vertical_space, Column, Container, PickList, Row, Rule, Slider, Text,
     Tooltip,
 };
 use iced::Length::Fixed;
@@ -261,7 +260,7 @@ fn mmdb_selection_row(
         .align_items(Alignment::Center)
         .push(Text::new(format!("{caption}: ")).font(font))
         .push(
-            Text::new(get_path_termination_string(custom_path, 30))
+            Text::new(get_path_termination_string(custom_path, 25))
                 .font(font)
                 .style(if is_error {
                     TextType::Danger
diff --git a/src/gui/pages/settings_style_page.rs b/src/gui/pages/settings_style_page.rs
index 1d7e90fdd..af9920319 100644
--- a/src/gui/pages/settings_style_page.rs
+++ b/src/gui/pages/settings_style_page.rs
@@ -315,7 +315,7 @@ fn lazy_custom_style_input(
     let button_row = Row::new()
         .align_items(Alignment::Center)
         .push(
-            Text::new(get_path_termination_string(custom_path, 20))
+            Text::new(get_path_termination_string(custom_path, 17))
                 .font(font)
                 .style(if is_error {
                     TextType::Danger
diff --git a/src/gui/pages/types/running_page.rs b/src/gui/pages/types/running_page.rs
index d4313506c..3a2db7e26 100644
--- a/src/gui/pages/types/running_page.rs
+++ b/src/gui/pages/types/running_page.rs
@@ -53,7 +53,7 @@ impl RunningPage {
         }
     }
 
-    pub fn icon(self) -> iced::advanced::widget::Text<'static, Renderer<StyleType>> {
+    pub fn icon(self) -> iced::widget::Text<'static, Renderer<StyleType>> {
         match self {
             RunningPage::Overview => Icon::Overview,
             RunningPage::Inspect => Icon::Inspect,
diff --git a/src/gui/pages/types/settings_page.rs b/src/gui/pages/types/settings_page.rs
index 43c712b35..7fe21ac7c 100644
--- a/src/gui/pages/types/settings_page.rs
+++ b/src/gui/pages/types/settings_page.rs
@@ -48,11 +48,11 @@ impl SettingsPage {
         }
     }
 
-    pub fn icon(self) -> iced::advanced::widget::Text<'static, Renderer<StyleType>> {
+    pub fn icon(self) -> iced::widget::Text<'static, Renderer<StyleType>> {
         match self {
             SettingsPage::Notifications => Icon::Notification,
             SettingsPage::Appearance => Icon::HalfSun,
-            SettingsPage::General => Icon::Dots,
+            SettingsPage::General => Icon::Generals,
         }
         .to_text()
     }
diff --git a/src/gui/styles/button.rs b/src/gui/styles/button.rs
index 350da99a4..7f0fda1df 100644
--- a/src/gui/styles/button.rs
+++ b/src/gui/styles/button.rs
@@ -26,6 +26,8 @@ pub enum ButtonType {
     Neutral,
     Alert,
     Gradient(GradientType),
+    SortArrows,
+    SortArrowActive,
 }
 
 impl button::StyleSheet for StyleType {
@@ -44,9 +46,10 @@ impl button::StyleSheet for StyleType {
                     a: ext.alpha_round_containers,
                     ..ext.buttons_color
                 }),
-                ButtonType::Neutral | ButtonType::NotStarred => {
-                    Background::Color(Color::TRANSPARENT)
-                }
+                ButtonType::Neutral
+                | ButtonType::NotStarred
+                | ButtonType::SortArrows
+                | ButtonType::SortArrowActive => Background::Color(Color::TRANSPARENT),
                 ButtonType::Gradient(GradientType::None) => Background::Color(colors.secondary),
                 ButtonType::Gradient(gradient_type) => Background::Gradient(get_gradient_buttons(
                     &colors,
@@ -66,6 +69,8 @@ impl button::StyleSheet for StyleType {
             border_width: match style {
                 ButtonType::TabActive
                 | ButtonType::TabInactive
+                | ButtonType::SortArrows
+                | ButtonType::SortArrowActive
                 | ButtonType::Starred
                 | ButtonType::NotStarred
                 | ButtonType::Neutral => 0.0,
@@ -78,6 +83,11 @@ impl button::StyleSheet for StyleType {
             },
             text_color: match style {
                 ButtonType::Starred => Color::BLACK,
+                ButtonType::SortArrows => Color {
+                    a: if ext.is_nightly { 0.2 } else { 0.7 },
+                    ..colors.text_body
+                },
+                ButtonType::SortArrowActive => colors.secondary,
                 ButtonType::Gradient(_) => colors.text_headers,
                 _ => colors.text_body,
             },
@@ -97,12 +107,17 @@ impl button::StyleSheet for StyleType {
         let ext = self.get_extension();
         button::Appearance {
             shadow_offset: match style {
-                ButtonType::Neutral => Vector::default(),
+                ButtonType::Neutral | ButtonType::SortArrows | ButtonType::SortArrowActive => {
+                    Vector::default()
+                }
                 ButtonType::TabActive | ButtonType::TabInactive => Vector::new(3.0, 3.0),
                 _ => Vector::new(0.0, 2.0),
             },
             background: Some(match style {
                 ButtonType::Starred => Background::Color(colors.starred),
+                ButtonType::SortArrows | ButtonType::SortArrowActive => {
+                    Background::Color(Color::TRANSPARENT)
+                }
                 ButtonType::Neutral => Background::Color(Color {
                     a: ext.alpha_round_borders,
                     ..ext.buttons_color
@@ -125,6 +140,8 @@ impl button::StyleSheet for StyleType {
             border_width: match style {
                 ButtonType::Starred
                 | ButtonType::TabActive
+                | ButtonType::SortArrows
+                | ButtonType::SortArrowActive
                 | ButtonType::TabInactive
                 | ButtonType::BorderedRound => 0.0,
                 _ => BORDER_WIDTH,
@@ -141,6 +158,7 @@ impl button::StyleSheet for StyleType {
             text_color: match style {
                 ButtonType::Starred => Color::BLACK,
                 ButtonType::Gradient(_) => colors.text_headers,
+                ButtonType::SortArrowActive | ButtonType::SortArrows => colors.secondary,
                 _ => colors.text_body,
             },
         }
diff --git a/src/gui/styles/checkbox.rs b/src/gui/styles/checkbox.rs
index fb21ca130..6c6fbf3cc 100644
--- a/src/gui/styles/checkbox.rs
+++ b/src/gui/styles/checkbox.rs
@@ -2,10 +2,10 @@
 
 #![allow(clippy::module_name_repetitions)]
 
-use crate::gui::styles::style_constants::BORDER_WIDTH;
 use iced::widget::checkbox::Appearance;
 use iced::Background;
 
+use crate::gui::styles::style_constants::BORDER_WIDTH;
 use crate::StyleType;
 
 #[derive(Clone, Copy, Default)]
diff --git a/src/gui/styles/mod.rs b/src/gui/styles/mod.rs
index 4d55a81b4..58d43505b 100644
--- a/src/gui/styles/mod.rs
+++ b/src/gui/styles/mod.rs
@@ -11,4 +11,5 @@ pub mod style_constants;
 pub mod svg;
 pub mod text;
 pub mod text_input;
+mod toggler;
 pub mod types;
diff --git a/src/gui/styles/picklist.rs b/src/gui/styles/picklist.rs
index b55d94578..5f36d612a 100644
--- a/src/gui/styles/picklist.rs
+++ b/src/gui/styles/picklist.rs
@@ -2,10 +2,10 @@
 
 #![allow(clippy::module_name_repetitions)]
 
-use crate::gui::styles::style_constants::BORDER_WIDTH;
 use iced::widget::pick_list;
 use iced::{Background, Color};
 
+use crate::gui::styles::style_constants::BORDER_WIDTH;
 use crate::gui::styles::types::palette::mix_colors;
 use crate::StyleType;
 
diff --git a/src/gui/styles/style_constants.rs b/src/gui/styles/style_constants.rs
index f5488f9e3..64fc09a7e 100644
--- a/src/gui/styles/style_constants.rs
+++ b/src/gui/styles/style_constants.rs
@@ -3,6 +3,7 @@
 use iced::font::{Family, Stretch, Weight};
 use iced::{Color, Font};
 
+use crate::gui::app::{FONT_FAMILY_NAME, ICON_FONT_FAMILY_NAME};
 use crate::gui::styles::types::palette::Palette;
 use crate::gui::styles::types::palette_extension::PaletteExtension;
 
@@ -192,7 +193,7 @@ pub const MON_AMOUR_PALETTE_EXTENSION: PaletteExtension = PaletteExtension {
 pub const SARASA_MONO_BOLD_BYTES: &[u8] =
     include_bytes!("../../../resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf");
 pub const SARASA_MONO_BOLD: Font = Font {
-    family: Family::Name("Sarasa Mono SC"),
+    family: Family::Name(FONT_FAMILY_NAME),
     weight: Weight::Bold,
     stretch: Stretch::Normal,
     monospaced: true,
@@ -201,7 +202,7 @@ pub const SARASA_MONO_BOLD: Font = Font {
 pub const SARASA_MONO_BYTES: &[u8] =
     include_bytes!("../../../resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf");
 pub const SARASA_MONO: Font = Font {
-    family: Family::Name("Sarasa Mono SC"),
+    family: Family::Name(FONT_FAMILY_NAME),
     weight: Weight::Normal,
     stretch: Stretch::Normal,
     monospaced: true,
@@ -209,7 +210,7 @@ pub const SARASA_MONO: Font = Font {
 
 //font to display icons
 pub const ICONS_BYTES: &[u8] = include_bytes!("../../../resources/fonts/subset/icons.ttf");
-pub const ICONS: Font = Font::with_name("Glyphter");
+pub const ICONS: Font = Font::with_name(ICON_FONT_FAMILY_NAME);
 
 // font sizes
 pub const FONT_SIZE_FOOTER: f32 = 14.3;
diff --git a/src/gui/styles/text_input.rs b/src/gui/styles/text_input.rs
index 395ac9473..c494068d2 100644
--- a/src/gui/styles/text_input.rs
+++ b/src/gui/styles/text_input.rs
@@ -2,10 +2,10 @@
 
 #![allow(clippy::module_name_repetitions)]
 
-use crate::gui::styles::style_constants::BORDER_WIDTH;
 use iced::widget::text_input::Appearance;
 use iced::{Background, Color};
 
+use crate::gui::styles::style_constants::BORDER_WIDTH;
 use crate::StyleType;
 
 #[derive(Clone, Copy, Default)]
@@ -39,12 +39,16 @@ impl iced::widget::text_input::StyleSheet for StyleType {
                 TextInputType::Standard => ext.buttons_color,
                 TextInputType::Error => Color::new(0.8, 0.15, 0.15, 1.0),
             },
-            icon_color: colors.text_body,
+            icon_color: Color {
+                a: if ext.is_nightly { 0.2 } else { 0.7 },
+                ..colors.text_body
+            },
         }
     }
 
     fn focused(&self, style: &Self::Style) -> iced::widget::text_input::Appearance {
         let colors = self.get_palette();
+        let is_nightly = self.get_extension().is_nightly;
         Appearance {
             background: Background::Color(colors.primary),
             border_radius: TEXT_INPUT_BORDER_RADIUS.into(),
@@ -53,7 +57,10 @@ impl iced::widget::text_input::StyleSheet for StyleType {
                 TextInputType::Error => Color::new(0.8, 0.15, 0.15, 1.0),
                 _ => colors.secondary,
             },
-            icon_color: colors.text_body,
+            icon_color: Color {
+                a: if is_nightly { 0.2 } else { 0.7 },
+                ..colors.text_body
+            },
         }
     }
 
@@ -102,7 +109,10 @@ impl iced::widget::text_input::StyleSheet for StyleType {
                 TextInputType::Error => Color::new(0.8, 0.15, 0.15, 1.0),
                 _ => colors.secondary,
             },
-            icon_color: colors.text_body,
+            icon_color: Color {
+                a: if ext.is_nightly { 0.2 } else { 0.7 },
+                ..colors.text_body
+            },
         }
     }
 
@@ -127,7 +137,10 @@ impl iced::widget::text_input::StyleSheet for StyleType {
                 },
                 TextInputType::Error => Color::new(0.8, 0.15, 0.15, ext.alpha_round_borders),
             },
-            icon_color: colors.text_body,
+            icon_color: Color {
+                a: if ext.is_nightly { 0.2 } else { 0.7 },
+                ..colors.text_body
+            },
         }
     }
 }
diff --git a/src/gui/styles/toggler.rs b/src/gui/styles/toggler.rs
new file mode 100644
index 000000000..73b6ca696
--- /dev/null
+++ b/src/gui/styles/toggler.rs
@@ -0,0 +1,62 @@
+//! Toggler style
+
+#![allow(clippy::module_name_repetitions)]
+
+use iced::widget::toggler::Appearance;
+use iced::Color;
+
+use crate::StyleType;
+
+#[derive(Clone, Copy, Default)]
+pub enum TogglerType {
+    #[default]
+    Standard,
+}
+
+impl iced::widget::toggler::StyleSheet for StyleType {
+    type Style = TogglerType;
+
+    fn active(&self, _: &Self::Style, is_active: bool) -> iced::widget::toggler::Appearance {
+        let colors = self.get_palette();
+        let ext = self.get_extension();
+        Appearance {
+            background: if is_active {
+                Color {
+                    a: ext.alpha_chart_badge,
+                    ..colors.secondary
+                }
+            } else {
+                ext.buttons_color
+            },
+            background_border: Some(ext.buttons_color),
+            foreground: colors.primary,
+            foreground_border: Some(if is_active {
+                colors.secondary
+            } else {
+                ext.buttons_color
+            }),
+        }
+    }
+
+    fn hovered(&self, _: &Self::Style, is_active: bool) -> iced::widget::toggler::Appearance {
+        let colors = self.get_palette();
+        let ext = self.get_extension();
+        Appearance {
+            background: if is_active {
+                Color {
+                    a: ext.alpha_chart_badge,
+                    ..colors.secondary
+                }
+            } else {
+                ext.buttons_color
+            },
+            background_border: Some(colors.secondary),
+            foreground: colors.primary,
+            foreground_border: Some(if is_active {
+                colors.secondary
+            } else {
+                ext.buttons_color
+            }),
+        }
+    }
+}
diff --git a/src/gui/types/sniffer.rs b/src/gui/types/sniffer.rs
index 1f3d82332..4410dd01e 100644
--- a/src/gui/types/sniffer.rs
+++ b/src/gui/types/sniffer.rs
@@ -112,7 +112,7 @@ impl Sniffer {
             pcap_error: None,
             waiting: ".".to_string(),
             traffic_chart: TrafficChart::new(style, language),
-            report_sort_type: ReportSortType::MostRecent,
+            report_sort_type: ReportSortType::default(),
             modal: None,
             settings_page: None,
             last_opened_setting: SettingsPage::Notifications,
@@ -157,7 +157,10 @@ impl Sniffer {
                 self.filters.port_str = value;
             }
             Message::ChartSelection(unit) => self.traffic_chart.change_kind(unit),
-            Message::ReportSortSelection(sort) => self.report_sort_type = sort,
+            Message::ReportSortSelection(sort) => {
+                self.page_number = 1;
+                self.report_sort_type = sort;
+            }
             Message::OpenWebPage(web_page) => Self::open_web(&web_page),
             Message::Start => self.start(),
             Message::Reset => return self.reset(),
@@ -406,7 +409,7 @@ impl Sniffer {
         self.running_page = RunningPage::Init;
         *self.current_capture_id.lock().unwrap() += 1; //change capture id to kill previous captures
         self.pcap_error = None;
-        self.report_sort_type = ReportSortType::MostRecent;
+        self.report_sort_type = ReportSortType::default();
         self.unread_notifications = 0;
         self.search = SearchParameters::default();
         self.page_number = 1;
@@ -622,6 +625,8 @@ mod tests {
         BytesNotification, FavoriteNotification, Notification, Notifications, PacketsNotification,
     };
     use crate::notifications::types::sound::Sound;
+    use crate::report::types::report_col::ReportCol;
+    use crate::report::types::sort_type::SortType;
     use crate::{
         ByteMultiple, ChartType, ConfigDevice, ConfigSettings, ConfigWindow, Configs, IpVersion,
         Language, Protocol, ReportSortType, RunningPage, Sniffer, StyleType,
@@ -717,18 +722,59 @@ mod tests {
 
     #[test]
     #[parallel] // needed to not collide with other tests generating configs files
-    fn test_correctly_update_report_kind() {
+    fn test_correctly_update_report_sort_kind() {
         let mut sniffer = new_sniffer();
 
-        assert_eq!(sniffer.report_sort_type, ReportSortType::MostRecent);
-        sniffer.update(Message::ReportSortSelection(ReportSortType::MostBytes));
-        assert_eq!(sniffer.report_sort_type, ReportSortType::MostBytes);
-        sniffer.update(Message::ReportSortSelection(ReportSortType::MostPackets));
-        assert_eq!(sniffer.report_sort_type, ReportSortType::MostPackets);
-        sniffer.update(Message::ReportSortSelection(ReportSortType::MostPackets));
-        assert_eq!(sniffer.report_sort_type, ReportSortType::MostPackets);
-        sniffer.update(Message::ReportSortSelection(ReportSortType::MostRecent));
-        assert_eq!(sniffer.report_sort_type, ReportSortType::MostRecent);
+        let sort = ReportSortType {
+            byte_sort: SortType::Neutral,
+            packet_sort: SortType::Neutral,
+        };
+
+        assert_eq!(sniffer.report_sort_type, sort);
+        sniffer.update(Message::ReportSortSelection(
+            sort.next_sort(&ReportCol::Bytes),
+        ));
+        assert_eq!(
+            sniffer.report_sort_type,
+            ReportSortType {
+                byte_sort: SortType::Descending,
+                packet_sort: SortType::Neutral
+            }
+        );
+        sniffer.update(Message::ReportSortSelection(
+            sort.next_sort(&ReportCol::Bytes)
+                .next_sort(&ReportCol::Bytes),
+        ));
+        assert_eq!(
+            sniffer.report_sort_type,
+            ReportSortType {
+                byte_sort: SortType::Ascending,
+                packet_sort: SortType::Neutral
+            }
+        );
+        sniffer.update(Message::ReportSortSelection(
+            sort.next_sort(&ReportCol::Bytes)
+                .next_sort(&ReportCol::Packets),
+        ));
+        assert_eq!(
+            sniffer.report_sort_type,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Descending
+            }
+        );
+        sniffer.update(Message::ReportSortSelection(
+            sort.next_sort(&ReportCol::Bytes)
+                .next_sort(&ReportCol::Bytes)
+                .next_sort(&ReportCol::Bytes),
+        ));
+        assert_eq!(
+            sniffer.report_sort_type,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Neutral
+            }
+        );
     }
 
     #[test]
diff --git a/src/main.rs b/src/main.rs
index 54886c14c..ca59887c9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -30,6 +30,7 @@ use utils::formatted_strings::print_cli_welcome_message;
 
 use crate::configs::types::config_window::{ConfigWindow, ToPosition};
 use crate::configs::types::configs::Configs;
+use crate::gui::app::FONT_FAMILY_NAME;
 use crate::secondary_threads::check_updates::set_newer_release_status;
 
 mod chart;
@@ -106,7 +107,7 @@ pub fn main() -> iced::Result {
             ..Default::default()
         },
         flags: Sniffer::new(&configs1, newer_release_available1),
-        default_font: Font::with_name("Sarasa Mono SC"),
+        default_font: Font::with_name(FONT_FAMILY_NAME),
         default_text_size: FONT_SIZE_BODY,
         antialiasing: false,
         exit_on_close_request: false,
diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs
index dde0d077f..a536b8816 100644
--- a/src/networking/manage_packets.rs
+++ b/src/networking/manage_packets.rs
@@ -344,11 +344,11 @@ fn get_traffic_direction(
     if my_interface_addresses_string.contains(source_ip) {
         // source is local
         TrafficDirection::Outgoing
-    } else if source_ip.ne("0.0.0.0") {
-        // source not local and different from 0.0.0.0
+    } else if source_ip.ne("0.0.0.0") && source_ip.ne("::") {
+        // source not local and different from 0.0.0.0 and different from ::
         TrafficDirection::Incoming
     } else if !my_interface_addresses_string.contains(destination_ip) {
-        // source is 0.0.0.0 (local not yet assigned an IP) and destination is not local
+        // source is 0.0.0.0 or :: (local not yet assigned an IP) and destination is not local
         TrafficDirection::Outgoing
     } else {
         TrafficDirection::Incoming
diff --git a/src/networking/types/address_port_pair.rs b/src/networking/types/address_port_pair.rs
index 47fabc1eb..6d75bf403 100644
--- a/src/networking/types/address_port_pair.rs
+++ b/src/networking/types/address_port_pair.rs
@@ -1,7 +1,5 @@
 //! Module defining the `AddressPortPair` struct, which represents a network address:port pair.
 
-use std::fmt;
-
 use crate::Protocol;
 
 /// Struct representing a network address:port pair.
@@ -43,37 +41,3 @@ impl AddressPortPair {
         }
     }
 }
-
-impl fmt::Display for AddressPortPair {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let (port_1_str, port_2_str) = if self.port1.is_some() && self.port2.is_some() {
-            (
-                self.port1.unwrap().to_string(),
-                self.port2.unwrap().to_string(),
-            )
-        } else {
-            ("-".to_string(), "-".to_string())
-        };
-        if self.address1.len() > 25 || self.address2.len() > 25 {
-            write!(
-                f,
-                "{:^45}{:>8}  {:^45}{:>8}    {:>4}   ",
-                self.address1,
-                port_1_str,
-                self.address2,
-                port_2_str,
-                self.protocol.to_string()
-            )
-        } else {
-            write!(
-                f,
-                "{:^25}{:>8}  {:^25}{:>8}    {:>4}   ",
-                self.address1,
-                port_1_str,
-                self.address2,
-                port_2_str,
-                self.protocol.to_string()
-            )
-        }
-    }
-}
diff --git a/src/networking/types/info_address_port_pair.rs b/src/networking/types/info_address_port_pair.rs
index 9c16e517b..01699b744 100644
--- a/src/networking/types/info_address_port_pair.rs
+++ b/src/networking/types/info_address_port_pair.rs
@@ -2,13 +2,11 @@
 //! to keep track of statistics about the sniffed traffic.
 
 use std::collections::HashMap;
-use std::fmt;
 
 use chrono::{DateTime, Local};
 
 use crate::networking::types::icmp_type::IcmpType;
 use crate::networking::types::traffic_direction::TrafficDirection;
-use crate::utils::formatted_strings::get_formatted_bytes_string;
 use crate::AppProtocol;
 
 /// Struct useful to format the output report file and to keep track of statistics about the sniffed traffic.
@@ -35,17 +33,3 @@ pub struct InfoAddressPortPair {
     /// Types of the ICMP messages exchanged, with the relative count (this is empty if not ICMP)
     pub icmp_types: HashMap<IcmpType, usize>,
 }
-
-impl fmt::Display for InfoAddressPortPair {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let bytes_string = get_formatted_bytes_string(self.transmitted_bytes);
-
-        write!(
-            f,
-            "{:^9}{:>10}  {:>9}   ",
-            self.app_protocol.to_string(),
-            self.transmitted_packets,
-            bytes_string,
-        )
-    }
-}
diff --git a/src/networking/types/search_parameters.rs b/src/networking/types/search_parameters.rs
index 233192ee8..4f7ec58ca 100644
--- a/src/networking/types/search_parameters.rs
+++ b/src/networking/types/search_parameters.rs
@@ -1,14 +1,26 @@
+use crate::networking::types::address_port_pair::AddressPortPair;
+use crate::networking::types::host::Host;
+use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
+
 /// Used to express the search filters applied to GUI inspect page
 #[derive(Clone, Debug, Default, Hash)]
 pub struct SearchParameters {
+    /// IP address (source)
+    pub address_src: String,
+    /// Transport port (source)
+    pub port_src: String,
+    /// IP address (destination)
+    pub address_dst: String,
+    /// Transport port (destination)
+    pub port_dst: String,
+    /// Protocol
+    pub proto: String,
     /// Application protocol
-    pub app: String,
-    /// IP address
-    pub address: String,
-    /// Domain
-    pub domain: String,
+    pub app_proto: String,
     /// Country
     pub country: String,
+    /// Domain
+    pub domain: String,
     /// Autonomous System name
     pub as_name: String,
     /// Whether to display only favorites
@@ -16,20 +28,237 @@ pub struct SearchParameters {
 }
 
 impl SearchParameters {
-    pub fn is_some_filter_active(&self) -> bool {
+    pub fn match_entry(
+        &self,
+        key: &AddressPortPair,
+        value: &InfoAddressPortPair,
+        r_dns_host: Option<&(String, Host)>,
+        is_favorite: bool,
+    ) -> bool {
+        // if a host-related filter is active and this address has not been resolved yet => false
+        if r_dns_host.is_none() && self.is_some_host_filter_active() {
+            return false;
+        }
+
+        for filter_input_type in FilterInputType::ALL {
+            if !filter_input_type.matches_entry(self, key, value, r_dns_host) {
+                return false;
+            }
+        }
+
+        // check favorites filter
+        if self.only_favorites && !is_favorite {
+            return false;
+        }
+
+        // if arrived at this point all filters are satisfied
+        true
+    }
+
+    pub fn is_some_host_filter_active(&self) -> bool {
         self.only_favorites
-            || !self.app.is_empty()
-            || !self.domain.is_empty()
             || !self.country.is_empty()
             || !self.as_name.is_empty()
-            || !self.address.is_empty()
+            || !self.domain.is_empty()
+    }
+
+    pub fn reset_host_filters(&self) -> Self {
+        Self {
+            country: String::new(),
+            domain: String::new(),
+            as_name: String::new(),
+            only_favorites: false,
+            ..self.clone()
+        }
     }
 }
 
+#[derive(Copy, Clone)]
 pub enum FilterInputType {
-    App,
-    Domain,
+    AddressSrc,
+    PortSrc,
+    AddressDst,
+    PortDst,
+    Proto,
+    AppProto,
     Country,
-    AS,
-    Address,
+    Domain,
+    AsName,
+}
+
+impl FilterInputType {
+    pub const ALL: [FilterInputType; 9] = [
+        Self::AddressSrc,
+        Self::PortSrc,
+        Self::AddressDst,
+        Self::PortDst,
+        Self::Proto,
+        Self::AppProto,
+        Self::Country,
+        Self::Domain,
+        Self::AsName,
+    ];
+
+    pub fn matches_entry(
+        self,
+        search_params: &SearchParameters,
+        key: &AddressPortPair,
+        value: &InfoAddressPortPair,
+        r_dns_host: Option<&(String, Host)>,
+    ) -> bool {
+        let filter_value = self.current_value(search_params).to_lowercase();
+
+        if filter_value.is_empty() {
+            return true;
+        }
+
+        let entry_value = self.entry_value(key, value, r_dns_host).to_lowercase();
+
+        if let Some(stripped_filter) = filter_value.strip_prefix('=') {
+            return entry_value.eq(stripped_filter);
+        }
+
+        match self {
+            FilterInputType::AddressSrc
+            | FilterInputType::AddressDst
+            | FilterInputType::Domain
+            | FilterInputType::AsName => entry_value.contains(&filter_value),
+            FilterInputType::PortSrc
+            | FilterInputType::PortDst
+            | FilterInputType::Proto
+            | FilterInputType::AppProto
+            | FilterInputType::Country => entry_value.starts_with(&filter_value),
+        }
+    }
+
+    pub fn current_value(self, search_params: &SearchParameters) -> &str {
+        match self {
+            FilterInputType::AddressSrc => &search_params.address_src,
+            FilterInputType::PortSrc => &search_params.port_src,
+            FilterInputType::AddressDst => &search_params.address_dst,
+            FilterInputType::PortDst => &search_params.port_dst,
+            FilterInputType::Proto => &search_params.proto,
+            FilterInputType::AppProto => &search_params.app_proto,
+            FilterInputType::Country => &search_params.country,
+            FilterInputType::Domain => &search_params.domain,
+            FilterInputType::AsName => &search_params.as_name,
+        }
+    }
+
+    pub fn entry_value(
+        self,
+        key: &AddressPortPair,
+        value: &InfoAddressPortPair,
+        r_dns_host: Option<&(String, Host)>,
+    ) -> String {
+        match self {
+            FilterInputType::AddressSrc => key.address1.to_string(),
+            FilterInputType::PortSrc => {
+                if let Some(port) = key.port1 {
+                    port.to_string()
+                } else {
+                    "-".to_string()
+                }
+            }
+            FilterInputType::AddressDst => key.address2.to_string(),
+            FilterInputType::PortDst => {
+                if let Some(port) = key.port2 {
+                    port.to_string()
+                } else {
+                    "-".to_string()
+                }
+            }
+            FilterInputType::Proto => key.protocol.to_string(),
+            FilterInputType::AppProto => value.app_protocol.to_string(),
+            FilterInputType::Country => r_dns_host.unwrap().1.country.to_string(),
+            FilterInputType::Domain => r_dns_host.unwrap().0.to_string(),
+            FilterInputType::AsName => r_dns_host.unwrap().1.asn.name.to_string(),
+        }
+    }
+
+    pub fn clear_search(self, search_params: &SearchParameters) -> SearchParameters {
+        match self {
+            FilterInputType::AddressSrc => SearchParameters {
+                address_src: String::new(),
+                ..search_params.clone()
+            },
+            FilterInputType::PortSrc => SearchParameters {
+                port_src: String::new(),
+                ..search_params.clone()
+            },
+            FilterInputType::AddressDst => SearchParameters {
+                address_dst: String::new(),
+                ..search_params.clone()
+            },
+            FilterInputType::PortDst => SearchParameters {
+                port_dst: String::new(),
+                ..search_params.clone()
+            },
+            FilterInputType::Proto => SearchParameters {
+                proto: String::new(),
+                ..search_params.clone()
+            },
+            FilterInputType::AppProto => SearchParameters {
+                app_proto: String::new(),
+                ..search_params.clone()
+            },
+            FilterInputType::Domain => SearchParameters {
+                domain: String::new(),
+                ..search_params.clone()
+            },
+            FilterInputType::Country => SearchParameters {
+                country: String::new(),
+                ..search_params.clone()
+            },
+            FilterInputType::AsName => SearchParameters {
+                as_name: String::new(),
+                ..search_params.clone()
+            },
+        }
+    }
+
+    pub fn new_search(
+        self,
+        search_params: &SearchParameters,
+        new_value: String,
+    ) -> SearchParameters {
+        match self {
+            FilterInputType::AddressSrc => SearchParameters {
+                address_src: new_value.trim().to_string(),
+                ..search_params.clone()
+            },
+            FilterInputType::PortSrc => SearchParameters {
+                port_src: new_value.trim().to_string(),
+                ..search_params.clone()
+            },
+            FilterInputType::AddressDst => SearchParameters {
+                address_dst: new_value.trim().to_string(),
+                ..search_params.clone()
+            },
+            FilterInputType::PortDst => SearchParameters {
+                port_dst: new_value.trim().to_string(),
+                ..search_params.clone()
+            },
+            FilterInputType::Proto => SearchParameters {
+                proto: new_value.trim().to_string(),
+                ..search_params.clone()
+            },
+            FilterInputType::AppProto => SearchParameters {
+                app_proto: new_value.trim().to_string(),
+                ..search_params.clone()
+            },
+            FilterInputType::Domain => SearchParameters {
+                domain: new_value.trim().to_string(),
+                ..search_params.clone()
+            },
+            FilterInputType::Country => SearchParameters {
+                country: new_value.trim().to_string(),
+                ..search_params.clone()
+            },
+            FilterInputType::AsName => SearchParameters {
+                as_name: new_value,
+                ..search_params.clone()
+            },
+        }
+    }
 }
diff --git a/src/notifications/types/sound.rs b/src/notifications/types/sound.rs
index 4eef17f21..a0c16d456 100644
--- a/src/notifications/types/sound.rs
+++ b/src/notifications/types/sound.rs
@@ -1,13 +1,13 @@
 use std::fmt;
 use std::thread;
 
-use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
 use iced::alignment::{Horizontal, Vertical};
 use iced::widget::Text;
 use iced::{Font, Length, Renderer};
 use rodio::{Decoder, OutputStream, Sink};
 use serde::{Deserialize, Serialize};
 
+use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
 use crate::notifications::types::sound::Sound::{Gulp, Pop, Swhoosh};
 use crate::utils::types::icon::Icon;
 use crate::StyleType;
@@ -43,10 +43,7 @@ impl Sound {
         }
     }
 
-    pub fn get_text(
-        self,
-        font: Font,
-    ) -> iced::advanced::widget::Text<'static, Renderer<StyleType>> {
+    pub fn get_text(self, font: Font) -> iced::widget::Text<'static, Renderer<StyleType>> {
         match self {
             Sound::Gulp => Text::new("Gulp").font(font),
             Sound::Pop => Text::new("Pop").font(font),
diff --git a/src/report/get_report_entries.rs b/src/report/get_report_entries.rs
index 0010df1ad..253579e96 100644
--- a/src/report/get_report_entries.rs
+++ b/src/report/get_report_entries.rs
@@ -1,24 +1,20 @@
 use std::cmp::{min, Ordering};
 use std::sync::{Arc, Mutex};
 
-use crate::countries::country_utils::get_flag_tooltip;
-use crate::countries::flags_pictures::FLAGS_WIDTH_SMALL;
 use crate::networking::manage_packets::get_address_to_lookup;
 use crate::networking::types::address_port_pair::AddressPortPair;
 use crate::networking::types::data_info::DataInfo;
 use crate::networking::types::data_info_host::DataInfoHost;
 use crate::networking::types::host::Host;
 use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
-use crate::report::types::report_entry::ReportEntry;
-use crate::{AppProtocol, ChartType, ConfigSettings, InfoTraffic, ReportSortType, Sniffer};
+use crate::report::types::sort_type::SortType;
+use crate::{AppProtocol, ChartType, InfoTraffic, ReportSortType, Sniffer};
 
 /// Returns the elements which satisfy the search constraints and belong to the given page,
 /// and the total number of elements which satisfy the search constraints
-pub fn get_searched_entries(sniffer: &Sniffer) -> (Vec<ReportEntry>, usize) {
-    let ConfigSettings {
-        style, language, ..
-    } = sniffer.configs.lock().unwrap().settings;
-
+pub fn get_searched_entries(
+    sniffer: &Sniffer,
+) -> (Vec<(AddressPortPair, InfoAddressPortPair)>, usize) {
     let info_traffic_lock = sniffer.info_traffic.lock().unwrap();
     let mut all_results: Vec<(&AddressPortPair, &InfoAddressPortPair)> = info_traffic_lock
         .map
@@ -26,74 +22,34 @@ pub fn get_searched_entries(sniffer: &Sniffer) -> (Vec<ReportEntry>, usize) {
         .filter(|(key, value)| {
             let address_to_lookup = &get_address_to_lookup(key, value.traffic_direction);
             let r_dns_host = info_traffic_lock.addresses_resolved.get(address_to_lookup);
-
-            let searched_domain = &*sniffer.search.domain.to_lowercase();
-            let searched_country = &*sniffer.search.country.to_lowercase();
-            let searched_as_name = &*sniffer.search.as_name.to_lowercase();
-            let searched_address = &*sniffer.search.address.to_lowercase();
-            let searched_only_fav = sniffer.search.only_favorites;
-            // if a host-related filter is active and this address has not been resolved yet => false
-            if r_dns_host.is_none()
-                && (!searched_domain.is_empty()
-                    || !searched_country.is_empty()
-                    || !searched_as_name.is_empty()
-                    || searched_only_fav)
-            {
-                return false;
-            }
-            // check application protocol filter
-            let searched_app = &*sniffer.search.app.to_lowercase();
-            let app = value.app_protocol.to_string().to_lowercase();
-            if !searched_app.is_empty() && app.ne(searched_app) {
-                return false;
-            }
-            // check domain filter
-            if !searched_domain.is_empty() {
-                let domain = r_dns_host.unwrap().0.to_lowercase();
-                if !domain.contains(searched_domain) {
-                    return false;
-                }
-            }
-            // check country filter
-            if !searched_country.is_empty() {
-                let country = r_dns_host.unwrap().1.country.to_string().to_lowercase();
-                if !country.starts_with(searched_country) {
-                    return false;
-                }
-            }
-            // check Autonomous System name filter
-            if !searched_as_name.is_empty() {
-                let asn_name = r_dns_host.unwrap().1.asn.name.to_lowercase();
-                if !asn_name.contains(searched_as_name) {
-                    return false;
-                }
-            }
-            // check IP address filter
-            if !searched_address.is_empty() {
-                let source = key.address1.to_lowercase();
-                let dest = key.address2.to_lowercase();
-                if !source.contains(searched_address) && !dest.contains(searched_address) {
-                    return false;
-                }
-            }
-            // check favorites filter
-            if searched_only_fav
-                && !info_traffic_lock
-                    .hosts
-                    .get(&r_dns_host.unwrap().1)
-                    .unwrap()
-                    .is_favorite
-            {
-                return false;
-            }
-            // if arrived at this point all filters are satisfied => return true
-            true
+            let is_favorite = if let Some(e) = r_dns_host {
+                info_traffic_lock.hosts.get(&e.1).unwrap().is_favorite
+            } else {
+                false
+            };
+            sniffer
+                .search
+                .match_entry(key, value, r_dns_host, is_favorite)
         })
         .collect();
     all_results.sort_by(|&(_, a), &(_, b)| match sniffer.report_sort_type {
-        ReportSortType::MostRecent => b.final_timestamp.cmp(&a.final_timestamp),
-        ReportSortType::MostBytes => b.transmitted_bytes.cmp(&a.transmitted_bytes),
-        ReportSortType::MostPackets => b.transmitted_packets.cmp(&a.transmitted_packets),
+        ReportSortType {
+            byte_sort,
+            packet_sort: SortType::Neutral,
+        } => match byte_sort {
+            SortType::Ascending => a.transmitted_bytes.cmp(&b.transmitted_bytes),
+            SortType::Descending => b.transmitted_bytes.cmp(&a.transmitted_bytes),
+            SortType::Neutral => b.final_timestamp.cmp(&a.final_timestamp),
+        },
+        ReportSortType {
+            byte_sort: SortType::Neutral,
+            packet_sort,
+        } => match packet_sort {
+            SortType::Ascending => a.transmitted_packets.cmp(&b.transmitted_packets),
+            SortType::Descending => b.transmitted_packets.cmp(&a.transmitted_packets),
+            SortType::Neutral => b.final_timestamp.cmp(&a.final_timestamp),
+        },
+        _ => b.final_timestamp.cmp(&a.final_timestamp),
     });
 
     let upper_bound = min(sniffer.page_number * 20, all_results.len());
@@ -103,33 +59,7 @@ pub fn get_searched_entries(sniffer: &Sniffer) -> (Vec<ReportEntry>, usize) {
             .get((sniffer.page_number - 1) * 20..upper_bound)
             .unwrap_or(&Vec::new())
             .iter()
-            .map(|key_val| {
-                let address_to_lookup =
-                    get_address_to_lookup(key_val.0, key_val.1.traffic_direction);
-                let host = info_traffic_lock
-                    .addresses_resolved
-                    .get(&address_to_lookup)
-                    .unwrap_or(&Default::default())
-                    .1
-                    .clone();
-                let default_host_info = &DataInfoHost::default();
-                let host_info = info_traffic_lock
-                    .hosts
-                    .get(&host)
-                    .unwrap_or(default_host_info);
-                let flag = get_flag_tooltip(
-                    host.country,
-                    FLAGS_WIDTH_SMALL,
-                    host_info,
-                    language,
-                    style.get_extension().font,
-                );
-                ReportEntry {
-                    key: key_val.0.clone(),
-                    val: key_val.1.clone(),
-                    tooltip: flag,
-                }
-            })
+            .map(|key_val| (key_val.0.clone(), key_val.1.clone()))
             .collect(),
         all_results.len(),
     )
diff --git a/src/report/types/mod.rs b/src/report/types/mod.rs
index 969bc7a7a..7c22d1c2a 100644
--- a/src/report/types/mod.rs
+++ b/src/report/types/mod.rs
@@ -1,2 +1,3 @@
-pub mod report_entry;
+pub mod report_col;
 pub mod report_sort_type;
+pub mod sort_type;
diff --git a/src/report/types/report_col.rs b/src/report/types/report_col.rs
new file mode 100644
index 000000000..99a62192f
--- /dev/null
+++ b/src/report/types/report_col.rs
@@ -0,0 +1,131 @@
+use crate::networking::types::address_port_pair::AddressPortPair;
+use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
+use crate::networking::types::search_parameters::FilterInputType;
+use crate::translations::translations::{
+    address_translation, application_protocol_translation, bytes_translation, packets_translation,
+    protocol_translation,
+};
+use crate::translations::translations_2::{destination_translation, source_translation};
+use crate::translations::translations_3::port_translation;
+use crate::translations::types::language::Language;
+use crate::utils::formatted_strings::get_formatted_bytes_string;
+
+// total width: 1012.0
+
+const LARGE_COL_WIDTH: f32 = 221.0;
+const SMALL_COL_WIDTH: f32 = 95.0;
+
+const LARGE_COL_MAX_CHARS: usize = 25;
+const SMALL_COL_MAX_CHARS: usize = 10;
+
+#[derive(Eq, PartialEq)]
+pub enum ReportCol {
+    SrcIp,
+    SrcPort,
+    DstIp,
+    DstPort,
+    Proto,
+    AppProto,
+    Bytes,
+    Packets,
+}
+
+impl ReportCol {
+    pub(crate) const ALL: [ReportCol; 8] = [
+        ReportCol::SrcIp,
+        ReportCol::SrcPort,
+        ReportCol::DstIp,
+        ReportCol::DstPort,
+        ReportCol::Proto,
+        ReportCol::AppProto,
+        ReportCol::Bytes,
+        ReportCol::Packets,
+    ];
+
+    pub(crate) fn get_title(&self, language: Language) -> String {
+        match self {
+            ReportCol::SrcIp | ReportCol::DstIp => address_translation(language).to_string(),
+            ReportCol::SrcPort | ReportCol::DstPort => port_translation(language).to_string(),
+            ReportCol::Proto => protocol_translation(language).to_string(),
+            ReportCol::AppProto => application_protocol_translation(language).to_string(),
+            ReportCol::Bytes => {
+                let mut str = bytes_translation(language).to_string();
+                str.remove(0).to_uppercase().to_string() + &str
+            }
+            ReportCol::Packets => {
+                let mut str = packets_translation(language).to_string();
+                str.remove(0).to_uppercase().to_string() + &str
+            }
+        }
+    }
+
+    pub(crate) fn get_title_direction_info(&self, language: Language) -> String {
+        match self {
+            ReportCol::SrcIp | ReportCol::SrcPort => {
+                format!(" ({})", source_translation(language).to_lowercase())
+            }
+            ReportCol::DstIp | ReportCol::DstPort => {
+                format!(" ({})", destination_translation(language).to_lowercase())
+            }
+            _ => String::new(),
+        }
+    }
+
+    pub(crate) fn get_value(&self, key: &AddressPortPair, val: &InfoAddressPortPair) -> String {
+        match self {
+            ReportCol::SrcIp => key.address1.clone(),
+            ReportCol::SrcPort => {
+                if let Some(port) = key.port1 {
+                    port.to_string()
+                } else {
+                    "-".to_string()
+                }
+            }
+            ReportCol::DstIp => key.address2.clone(),
+            ReportCol::DstPort => {
+                if let Some(port) = key.port2 {
+                    port.to_string()
+                } else {
+                    "-".to_string()
+                }
+            }
+            ReportCol::Proto => key.protocol.to_string(),
+            ReportCol::AppProto => val.app_protocol.to_string(),
+            ReportCol::Bytes => get_formatted_bytes_string(val.transmitted_bytes, 1),
+            ReportCol::Packets => val.transmitted_packets.to_string(),
+        }
+    }
+
+    pub(crate) fn get_width(&self) -> f32 {
+        match self {
+            ReportCol::SrcIp | ReportCol::DstIp => LARGE_COL_WIDTH,
+            _ => SMALL_COL_WIDTH,
+        }
+    }
+
+    pub(crate) fn get_max_chars(&self, language_opt: Option<Language>) -> usize {
+        let reduction_factor = if [Language::JA, Language::KO, Language::ZH]
+            .contains(&language_opt.unwrap_or(Language::EN))
+        {
+            2
+        } else {
+            1
+        };
+        match self {
+            ReportCol::SrcIp | ReportCol::DstIp => LARGE_COL_MAX_CHARS / reduction_factor,
+            _ => SMALL_COL_MAX_CHARS / reduction_factor,
+        }
+    }
+
+    pub(crate) fn get_filter_input_type(&self) -> FilterInputType {
+        match self {
+            ReportCol::SrcIp => FilterInputType::AddressSrc,
+            ReportCol::DstIp => FilterInputType::AddressDst,
+            ReportCol::SrcPort => FilterInputType::PortSrc,
+            ReportCol::DstPort => FilterInputType::PortDst,
+            ReportCol::Proto => FilterInputType::Proto,
+            ReportCol::AppProto => FilterInputType::AppProto,
+            ReportCol::Bytes | ReportCol::Packets => FilterInputType::Country, // just to not panic...
+        }
+    }
+}
diff --git a/src/report/types/report_entry.rs b/src/report/types/report_entry.rs
deleted file mode 100644
index 362aefe05..000000000
--- a/src/report/types/report_entry.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use iced::widget::Tooltip;
-use iced::Renderer;
-
-use crate::gui::types::message::Message;
-use crate::networking::types::address_port_pair::AddressPortPair;
-use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
-use crate::StyleType;
-
-/// Struct embedding all the info needed to build a row of the connections report
-pub struct ReportEntry {
-    pub key: AddressPortPair,
-    pub val: InfoAddressPortPair,
-    pub tooltip: Tooltip<'static, Message, Renderer<StyleType>>,
-}
diff --git a/src/report/types/report_sort_type.rs b/src/report/types/report_sort_type.rs
index a20674e52..39473fb56 100644
--- a/src/report/types/report_sort_type.rs
+++ b/src/report/types/report_sort_type.rs
@@ -1,31 +1,216 @@
-use crate::translations::translations::{
-    bytes_report_translation, packets_report_translation, recent_report_translation,
-};
-use crate::Language;
+use std::fmt::Debug;
 
-/// Enum representing the possible kinds of displayed relevant connections.
+use iced::widget::Text;
+use iced::Renderer;
+
+use crate::gui::styles::button::ButtonType;
+use crate::gui::styles::types::style_type::StyleType;
+use crate::report::types::report_col::ReportCol;
+use crate::report::types::sort_type::SortType;
+use crate::utils::types::icon::Icon;
+
+/// Struct representing the possible kinds of sort for displayed relevant connections.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-#[allow(clippy::enum_variant_names)]
-pub enum ReportSortType {
-    MostRecent,
-    MostBytes,
-    MostPackets,
+pub struct ReportSortType {
+    pub byte_sort: SortType,
+    pub packet_sort: SortType,
 }
 
 impl ReportSortType {
-    pub fn all_strings(language: Language) -> Vec<&'static str> {
-        vec![
-            recent_report_translation(language),
-            bytes_report_translation(language),
-            packets_report_translation(language),
-        ]
+    pub fn next_sort(self, report_col: &ReportCol) -> Self {
+        match report_col {
+            ReportCol::Bytes => Self {
+                byte_sort: match self.byte_sort {
+                    SortType::Ascending => SortType::Neutral,
+                    SortType::Descending => SortType::Ascending,
+                    SortType::Neutral => SortType::Descending,
+                },
+                packet_sort: SortType::Neutral,
+            },
+            ReportCol::Packets => Self {
+                byte_sort: SortType::Neutral,
+                packet_sort: match self.packet_sort {
+                    SortType::Ascending => SortType::Neutral,
+                    SortType::Descending => SortType::Ascending,
+                    SortType::Neutral => SortType::Descending,
+                },
+            },
+            _ => Self::default(),
+        }
     }
 
-    pub fn get_picklist_label(self, language: Language) -> &'static str {
-        match self {
-            ReportSortType::MostRecent => recent_report_translation(language),
-            ReportSortType::MostBytes => bytes_report_translation(language),
-            ReportSortType::MostPackets => packets_report_translation(language),
+    pub fn icon(self, report_col: &ReportCol) -> Text<'static, Renderer<StyleType>> {
+        let mut size = 14;
+        match report_col {
+            ReportCol::Bytes => match self.byte_sort {
+                SortType::Ascending => Icon::SortAscending,
+                SortType::Descending => Icon::SortDescending,
+                SortType::Neutral => {
+                    size = 18;
+                    Icon::SortNeutral
+                }
+            },
+            ReportCol::Packets => match self.packet_sort {
+                SortType::Ascending => Icon::SortAscending,
+                SortType::Descending => Icon::SortDescending,
+                SortType::Neutral => {
+                    size = 18;
+                    Icon::SortNeutral
+                }
+            },
+            _ => Icon::SortNeutral,
         }
+        .to_text()
+        .size(size)
+    }
+
+    pub fn button_type(self, report_col: &ReportCol) -> ButtonType {
+        match report_col {
+            ReportCol::Bytes => match self.byte_sort {
+                SortType::Ascending | SortType::Descending => ButtonType::SortArrowActive,
+                SortType::Neutral => ButtonType::SortArrows,
+            },
+            ReportCol::Packets => match self.packet_sort {
+                SortType::Ascending | SortType::Descending => ButtonType::SortArrowActive,
+                SortType::Neutral => ButtonType::SortArrows,
+            },
+            _ => ButtonType::SortArrows,
+        }
+    }
+}
+
+impl Default for ReportSortType {
+    fn default() -> Self {
+        Self {
+            byte_sort: SortType::Neutral,
+            packet_sort: SortType::Neutral,
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::report::types::report_col::ReportCol;
+    use crate::report::types::report_sort_type::ReportSortType;
+    use crate::report::types::sort_type::SortType;
+
+    #[test]
+    fn test_next_report_sort() {
+        let mut sort = ReportSortType::default();
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Neutral
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Packets);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Descending
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Packets);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Ascending
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Packets);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Neutral
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Packets);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Descending
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Bytes);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Descending,
+                packet_sort: SortType::Neutral
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Packets);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Descending
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Bytes);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Descending,
+                packet_sort: SortType::Neutral
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Bytes);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Ascending,
+                packet_sort: SortType::Neutral
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Packets);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Descending
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Bytes);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Descending,
+                packet_sort: SortType::Neutral
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Bytes);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Ascending,
+                packet_sort: SortType::Neutral
+            }
+        );
+
+        sort = sort.next_sort(&ReportCol::Bytes);
+        assert_eq!(
+            sort,
+            ReportSortType {
+                byte_sort: SortType::Neutral,
+                packet_sort: SortType::Neutral
+            }
+        );
     }
 }
diff --git a/src/report/types/sort_type.rs b/src/report/types/sort_type.rs
new file mode 100644
index 000000000..faa92651d
--- /dev/null
+++ b/src/report/types/sort_type.rs
@@ -0,0 +1,6 @@
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum SortType {
+    Ascending,
+    Descending,
+    Neutral,
+}
diff --git a/src/translations/translations.rs b/src/translations/translations.rs
index 8ca8b197e..f08b16961 100644
--- a/src/translations/translations.rs
+++ b/src/translations/translations.rs
@@ -1,3 +1,5 @@
+// EXTRA NEEDED CHARACTERS: Б
+
 use iced::widget::Text;
 use iced::Renderer;
 
@@ -167,28 +169,28 @@ pub fn ip_version_translation(language: Language) -> &'static str {
     }
 }
 
-pub fn transport_protocol_translation(language: Language) -> &'static str {
-    match language {
-        Language::EN => "Transport protocol",
-        Language::IT => "Protocollo di trasporto",
-        Language::FR => "Protocole de transport",
-        Language::ES | Language::PT => "Protocolo de transporte",
-        Language::PL => "Protokół transportowy",
-        Language::DE => "Netzwerkprotokoll",
-        Language::UK => "Транспортний протокол",
-        Language::ZH => "目标传输协议",
-        Language::RO => "Protocol de transport",
-        Language::KO => "전송 프로토콜",
-        Language::TR => "İletişim protokolü",
-        Language::RU => "Транспортный протокол",
-        Language::EL => "Πρωτόκολλο μεταφοράς",
-        // Language::FA => "پیوندنامهٔ ترابرد",
-        Language::SV => "Transportprotokoll",
-        Language::FI => "Kuljetusprotokolla",
-        Language::JA => "トランスポート プロトコル",
-        Language::UZ => "Transport protokoli",
-    }
-}
+// pub fn transport_protocol_translation(language: Language) -> &'static str {
+//     match language {
+//         Language::EN => "Transport protocol",
+//         Language::IT => "Protocollo di trasporto",
+//         Language::FR => "Protocole de transport",
+//         Language::ES | Language::PT => "Protocolo de transporte",
+//         Language::PL => "Protokół transportowy",
+//         Language::DE => "Netzwerkprotokoll",
+//         Language::UK => "Транспортний протокол",
+//         Language::ZH => "目标传输协议",
+//         Language::RO => "Protocol de transport",
+//         Language::KO => "전송 프로토콜",
+//         Language::TR => "İletişim protokolü",
+//         Language::RU => "Транспортный протокол",
+//         Language::EL => "Πρωτόκολλο μεταφοράς",
+//         // Language::FA => "پیوندنامهٔ ترابرد",
+//         Language::SV => "Transportprotokoll",
+//         Language::FI => "Kuljetusprotokolla",
+//         Language::JA => "トランスポート プロトコル",
+//         Language::UZ => "Transport protokoli",
+//     }
+// }
 
 pub fn protocol_translation(language: Language) -> &'static str {
     match language {
@@ -378,7 +380,7 @@ pub fn ask_clear_all_translation(language: Language) -> Text<'static, Renderer<S
 pub fn clear_all_translation(language: Language) -> String {
     match language {
         Language::EN => "Clear all".to_string(),
-        Language::IT => "Elimina tutte".to_string(),
+        Language::IT => "Elimina tutto".to_string(),
         Language::FR => "Tout effacer".to_string(),
         Language::ES => "Borrar todo".to_string(),
         Language::PL => "Wyczyść wszystko".to_string(),
@@ -1024,77 +1026,77 @@ pub fn bytes_chart_translation(language: Language) -> &'static str {
     }
 }
 
-pub fn recent_report_translation(language: Language) -> &'static str {
-    match language {
-        Language::EN => "most recent",
-        Language::IT => "più recenti",
-        Language::FR => "la plus récente",
-        Language::ES => "más reciente",
-        Language::PL => "najnowsze",
-        Language::DE => "zuletzt",
-        Language::UK => "найновіші",
-        Language::ZH => "按时间",
-        Language::RO => "cea mai recentă",
-        Language::KO => "가장 최근",
-        Language::TR => "en son",
-        Language::RU => "новейшие",
-        Language::PT => "mais recente",
-        Language::EL => "πιο πρόσφατα",
-        // Language::FA => "آخرین",
-        Language::SV => "senaste",
-        Language::FI => "viimeisin",
-        Language::JA => "新しい順",
-        Language::UZ => "eng so'nggi",
-    }
-}
+// pub fn recent_report_translation(language: Language) -> &'static str {
+//     match language {
+//         Language::EN => "most recent",
+//         Language::IT => "più recenti",
+//         Language::FR => "la plus récente",
+//         Language::ES => "más reciente",
+//         Language::PL => "najnowsze",
+//         Language::DE => "zuletzt",
+//         Language::UK => "найновіші",
+//         Language::ZH => "按时间",
+//         Language::RO => "cea mai recentă",
+//         Language::KO => "가장 최근",
+//         Language::TR => "en son",
+//         Language::RU => "новейшие",
+//         Language::PT => "mais recente",
+//         Language::EL => "πιο πρόσφατα",
+//         // Language::FA => "آخرین",
+//         Language::SV => "senaste",
+//         Language::FI => "viimeisin",
+//         Language::JA => "新しい順",
+//         Language::UZ => "eng so'nggi",
+//     }
+// }
 
-pub fn packets_report_translation(language: Language) -> &'static str {
-    match language {
-        Language::EN => "most packets",
-        Language::IT => "più pacchetti",
-        Language::FR => "le plus de paquets",
-        Language::ES => "mayoría de los paquetes",
-        Language::PL => "najwięcej pakietów",
-        Language::DE => "meiste Pakete",
-        Language::UK => "найбільше пакетів",
-        Language::ZH => "按数据包",
-        Language::RO => "cele mai multe pachete",
-        Language::KO => "대부분의 패킷",
-        Language::TR => "en çok paket",
-        Language::RU => "больше всего пакетов",
-        Language::PT => "mais pacotes",
-        Language::EL => "περισσότερα πακέτα",
-        // Language::FA => "بیشترین بسته ها",
-        Language::SV => "flest paket",
-        Language::FI => "eniten paketteja",
-        Language::JA => "パケット数の多い順",
-        Language::UZ => "eng ko'p paketlar",
-    }
-}
+// pub fn packets_report_translation(language: Language) -> &'static str {
+//     match language {
+//         Language::EN => "most packets",
+//         Language::IT => "più pacchetti",
+//         Language::FR => "le plus de paquets",
+//         Language::ES => "mayoría de los paquetes",
+//         Language::PL => "najwięcej pakietów",
+//         Language::DE => "meiste Pakete",
+//         Language::UK => "найбільше пакетів",
+//         Language::ZH => "按数据包",
+//         Language::RO => "cele mai multe pachete",
+//         Language::KO => "대부분의 패킷",
+//         Language::TR => "en çok paket",
+//         Language::RU => "больше всего пакетов",
+//         Language::PT => "mais pacotes",
+//         Language::EL => "περισσότερα πακέτα",
+//         // Language::FA => "بیشترین بسته ها",
+//         Language::SV => "flest paket",
+//         Language::FI => "eniten paketteja",
+//         Language::JA => "パケット数の多い順",
+//         Language::UZ => "eng ko'p paketlar",
+//     }
+// }
 
-pub fn bytes_report_translation(language: Language) -> &'static str {
-    match language {
-        Language::EN => "most bytes",
-        Language::IT => "più byte",
-        Language::FR => "le plus de données",
-        Language::ES => "mayoría de los bytes",
-        Language::PL => "najwięcej bajtów",
-        Language::DE => "meiste Bytes",
-        Language::UK => "найбільше байтів",
-        Language::ZH => "按流量",
-        Language::RO => "cei mai mulți octeți",
-        Language::KO => "대부분의 바이트",
-        Language::TR => "en çok bayt",
-        Language::RU => "больше всего байт",
-        Language::PT => "mais bytes",
-        Language::EL => "περισσότερα bytes",
-        // Language::FA => "بیشترین بایت ها",
-        Language::SV => "flest bytes",
-        Language::FI => "eniten tavuja",
-        Language::JA => "バイト量の多い順",
-        Language::UZ => "eng ko'p bayt",
-    }
-}
+// pub fn bytes_report_translation(language: Language) -> &'static str {
+//     match language {
+//         Language::EN => "most bytes",
+//         Language::IT => "più byte",
+//         Language::FR => "le plus de données",
+//         Language::ES => "mayoría de los bytes",
+//         Language::PL => "najwięcej bajtów",
+//         Language::DE => "meiste Bytes",
+//         Language::UK => "найбільше байтів",
+//         Language::ZH => "按流量",
+//         Language::RO => "cei mai mulți octeți",
+//         Language::KO => "대부분의 바이트",
+//         Language::TR => "en çok bayt",
+//         Language::RU => "больше всего байт",
+//         Language::PT => "mais bytes",
+//         Language::EL => "περισσότερα bytes",
+//         // Language::FA => "بیشترین بایت ها",
+//         Language::SV => "flest bytes",
+//         Language::FI => "eniten tavuja",
+//         Language::JA => "バイト量の多い順",
+//         Language::UZ => "eng ko'p bayt",
+//     }
+// }
 
 // pub fn favorite_report_translation(language: Language) -> &'static str {
 //     match language {
diff --git a/src/translations/translations_2.rs b/src/translations/translations_2.rs
index 03d91c621..f51aac08a 100644
--- a/src/translations/translations_2.rs
+++ b/src/translations/translations_2.rs
@@ -170,28 +170,28 @@ pub fn only_top_30_hosts_translation(language: Language) -> &'static str {
     }
 }
 
-pub fn sort_by_translation(language: Language) -> &'static str {
-    match language {
-        Language::EN => "Sort by",
-        Language::IT => "Ordina per",
-        Language::RU => "Сортировка",
-        Language::SV => "Sortera efter",
-        Language::FI => "Järjestä",
-        Language::DE => "Sortieren nach",
-        Language::TR => "Şuna göre sırala",
-        // Language::FA => "مرتب سازی بر اساس",
-        Language::ES | Language::PT => "Ordenar por",
-        Language::KO => "정렬",
-        Language::ZH => "排序",
-        Language::UK => "Сортувати за",
-        Language::RO => "Filtrează după",
-        Language::PL => "Sortuj według",
-        Language::FR => "Trier par",
-        Language::JA => "ソート",
-        Language::UZ => "Saralash turi",
-        _ => "Sort by",
-    }
-}
+// pub fn sort_by_translation(language: Language) -> &'static str {
+//     match language {
+//         Language::EN => "Sort by",
+//         Language::IT => "Ordina per",
+//         Language::RU => "Сортировка",
+//         Language::SV => "Sortera efter",
+//         Language::FI => "Järjestä",
+//         Language::DE => "Sortieren nach",
+//         Language::TR => "Şuna göre sırala",
+//         // Language::FA => "مرتب سازی بر اساس",
+//         Language::ES | Language::PT => "Ordenar por",
+//         Language::KO => "정렬",
+//         Language::ZH => "排序",
+//         Language::UK => "Сортувати за",
+//         Language::RO => "Filtrează după",
+//         Language::PL => "Sortuj według",
+//         Language::FR => "Trier par",
+//         Language::JA => "ソート",
+//         Language::UZ => "Saralash turi",
+//         _ => "Sort by",
+//     }
+// }
 
 pub fn local_translation(language: Language) -> String {
     match language {
@@ -504,29 +504,29 @@ pub fn only_show_favorites_translation(language: Language) -> &'static str {
     }
 }
 
-pub fn search_filters_translation(language: Language) -> &'static str {
-    match language {
-        Language::EN => "Search filters",
-        Language::IT => "Filtri di ricerca",
-        Language::RU => "Фильтры для поиска",
-        Language::SV => "Sökfilter",
-        Language::FI => "Hakusuodattimet",
-        Language::DE => "Filter suchen",
-        Language::TR => "Arama filtresi",
-        // Language::FA => "صافی های جستجو",
-        Language::ES => "Filtros de búsqueda",
-        Language::KO => "검색 필터",
-        Language::ZH => "搜索条件",
-        Language::UK => "Фільтри пошуку",
-        Language::RO => "Filtre de căutare",
-        Language::PL => "Filtry wyszukiwania",
-        Language::FR => "Filtres de recherche",
-        Language::JA => "検索フィルター",
-        Language::UZ => "Qidiruv filtrlari",
-        Language::PT => "Filtros de busca",
-        _ => "Search filters",
-    }
-}
+// pub fn search_filters_translation(language: Language) -> &'static str {
+//     match language {
+//         Language::EN => "Search filters",
+//         Language::IT => "Filtri di ricerca",
+//         Language::RU => "Фильтры для поиска",
+//         Language::SV => "Sökfilter",
+//         Language::FI => "Hakusuodattimet",
+//         Language::DE => "Filter suchen",
+//         Language::TR => "Arama filtresi",
+//         // Language::FA => "صافی های جستجو",
+//         Language::ES => "Filtros de búsqueda",
+//         Language::KO => "검색 필터",
+//         Language::ZH => "搜索条件",
+//         Language::UK => "Фільтри пошуку",
+//         Language::RO => "Filtre de căutare",
+//         Language::PL => "Filtry wyszukiwania",
+//         Language::FR => "Filtres de recherche",
+//         Language::JA => "検索フィルター",
+//         Language::UZ => "Qidiruv filtrlari",
+//         Language::PT => "Filtros de busca",
+//         _ => "Search filters",
+//     }
+// }
 
 pub fn no_search_results_translation(language: Language) -> &'static str {
     match language {
diff --git a/src/translations/translations_3.rs b/src/translations/translations_3.rs
index 3581f33af..2e754360d 100644
--- a/src/translations/translations_3.rs
+++ b/src/translations/translations_3.rs
@@ -124,3 +124,11 @@ pub fn database_from_file_translation(language: Language) -> &'static str {
         _ => "Select database file",
     }
 }
+
+pub fn filter_by_host_translation(language: Language) -> &'static str {
+    match language {
+        Language::EN => "Filter by network host",
+        Language::IT => "Filtra per host di rete",
+        _ => "Filter by network host",
+    }
+}
diff --git a/src/utils/formatted_strings.rs b/src/utils/formatted_strings.rs
index 615e9ddd2..cbceff418 100644
--- a/src/utils/formatted_strings.rs
+++ b/src/utils/formatted_strings.rs
@@ -78,7 +78,7 @@ pub fn get_active_filters_string(filters: &Filters, language: Language) -> Strin
 }
 
 /// Returns a String representing a quantity of bytes with its proper multiple (K, M, G, T)
-pub fn get_formatted_bytes_string(bytes: u128) -> String {
+pub fn get_formatted_bytes_string(bytes: u128, precision: usize) -> String {
     let mut multiple_transmitted = String::new();
     #[allow(clippy::cast_precision_loss)]
     let mut n = bytes as f32;
@@ -105,16 +105,20 @@ pub fn get_formatted_bytes_string(bytes: u128) -> String {
 
     if multiple_transmitted.is_empty() {
         // no multiple
-        format!("{n}  ")
+        n.to_string()
     } else {
         // with multiple
-        format!("{n:.1} {multiple_transmitted}")
+        format!("{n:.precision$} {multiple_transmitted}")
     }
 }
 
-/// Returns a String representing a quantity of bytes with its proper multiple (KB, MB, GB, TB)
-pub fn get_formatted_bytes_string_with_b(bytes: u128) -> String {
-    let mut bytes_string = get_formatted_bytes_string(bytes).replace("  ", " ");
+/// Returns a String representing a quantity of bytes with its proper multiple (B, KB, MB, GB, TB)
+pub fn get_formatted_bytes_string_with_b(bytes: u128, precision: usize) -> String {
+    let mut bytes_string = get_formatted_bytes_string(bytes, precision);
+    if bytes_string.parse::<f32>().is_ok() {
+        // no multiple
+        bytes_string.push(' ');
+    }
     bytes_string.push('B');
     bytes_string
 }
@@ -215,11 +219,17 @@ pub fn get_socket_address(address: &String, port: Option<u16>) -> String {
 }
 
 pub fn get_path_termination_string(full_path: &str, i: usize) -> String {
-    if full_path.is_empty() {
+    let chars = full_path.chars().collect::<Vec<char>>();
+    if chars.is_empty() {
         return String::new();
     }
-    let tot_len = full_path.len();
+    let tot_len = chars.len();
     let slice_len = min(i, tot_len);
-    let suspensions = if tot_len > i { "..." } else { "" };
-    [suspensions, &full_path[tot_len - slice_len..], " "].concat()
+    let suspensions = if tot_len > i { "…" } else { "" };
+    [
+        suspensions,
+        &chars[tot_len - slice_len..].iter().collect::<String>(),
+        " ",
+    ]
+    .concat()
 }
diff --git a/src/utils/types/icon.rs b/src/utils/types/icon.rs
index 12350388c..6b2d60445 100644
--- a/src/utils/types/icon.rs
+++ b/src/utils/types/icon.rs
@@ -15,7 +15,7 @@ pub enum Icon {
     BytesThreshold,
     Clock,
     Copy,
-    Dots,
+    Generals,
     Error,
     File,
     Forbidden,
@@ -35,52 +35,61 @@ pub enum Icon {
     Rocket,
     Settings,
     Sniffnet,
+    SortAscending,
+    SortDescending,
+    SortNeutral,
     Star,
     Warning,
     Waves,
 }
 
 impl Icon {
-    pub fn to_text(&self) -> iced::advanced::widget::Text<'static, Renderer<StyleType>> {
-        Text::new(match self {
-            Icon::ArrowBack => "C",
-            Icon::ArrowLeft => "i",
-            Icon::ArrowRight => "j",
-            Icon::ArrowsDown => ":",
-            Icon::AudioHigh => "Z",
-            Icon::AudioMute => "Y",
-            Icon::Bin => "h",
-            Icon::BytesThreshold => "f",
-            Icon::Clock => "9",
-            Icon::Dots => "R",
-            Icon::Error => "U",
-            Icon::File => "8",
-            Icon::Forbidden => "x",
-            Icon::Funnel => "V",
-            Icon::GitHub => "H",
-            Icon::Globe => "c",
-            Icon::HalfSun => "K",
-            Icon::Hourglass1 => "1",
-            Icon::Hourglass2 => "2",
-            Icon::Hourglass3 => "3",
-            Icon::Inspect => "5",
-            Icon::Lightning => "z",
-            Icon::Notification => "7",
-            Icon::Overview => "d",
-            Icon::PacketsThreshold => "e",
-            // Icon::Restore => "k",
-            Icon::Rocket => "S",
-            Icon::Settings => "a",
-            Icon::Sniffnet => "A",
-            Icon::Star => "g",
-            Icon::Warning => "T",
-            Icon::Waves => "y",
-            Icon::Copy => "u",
-        })
-        .font(ICONS)
+    pub fn codepoint(&self) -> char {
+        match self {
+            Icon::ArrowBack => 'C',
+            Icon::ArrowLeft => 'i',
+            Icon::ArrowRight => 'j',
+            Icon::ArrowsDown => ':',
+            Icon::AudioHigh => 'Z',
+            Icon::AudioMute => 'Y',
+            Icon::Bin => 'h',
+            Icon::BytesThreshold => 'f',
+            Icon::Clock => '9',
+            Icon::Generals => 'Q',
+            Icon::Error => 'U',
+            Icon::File => '8',
+            Icon::Forbidden => 'x',
+            Icon::Funnel => 'V',
+            Icon::GitHub => 'H',
+            Icon::Globe => 'c',
+            Icon::HalfSun => 'K',
+            Icon::Hourglass1 => '1',
+            Icon::Hourglass2 => '2',
+            Icon::Hourglass3 => '3',
+            Icon::Inspect => '5',
+            Icon::Lightning => 'z',
+            Icon::Notification => '7',
+            Icon::Overview => 'd',
+            Icon::PacketsThreshold => 'e',
+            // Icon::Restore => 'k',
+            Icon::Rocket => 'S',
+            Icon::Settings => 'a',
+            Icon::Sniffnet => 'A',
+            Icon::Star => 'g',
+            Icon::Warning => 'T',
+            Icon::Waves => 'y',
+            Icon::Copy => 'u',
+            Icon::SortAscending => 'm',
+            Icon::SortDescending => 'l',
+            Icon::SortNeutral => 'n',
+        }
+    }
+
+    pub fn to_text(&self) -> iced::widget::Text<'static, Renderer<StyleType>> {
+        Text::new(self.codepoint().to_string()).font(ICONS)
     }
 
-    pub fn get_hourglass(num: usize) -> iced::advanced::widget::Text<'static, Renderer<StyleType>> {
+    pub fn get_hourglass(num: usize) -> iced::widget::Text<'static, Renderer<StyleType>> {
         match num {
             1 => Icon::Hourglass1.to_text(),
             2 => Icon::Hourglass2.to_text(),


Test patch: