Skip to content

Commit fac671a

Browse files
create custom tab and implemented it
this will provide more customization flexibility as the iced_aw variant is quite limited.
1 parent c6085e1 commit fac671a

14 files changed

+221
-34
lines changed

assets/icons/binoculars-fill.svg

+3
Loading

assets/icons/card-checklist.svg

+4
Loading

assets/icons/film.svg

+3
Loading

assets/icons/gear-wide-connected.svg

+3
Loading

assets/icons/graph-up-arrow.svg

+3
Loading

src/gui/assets.rs

+9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ pub fn get_static_cow_from_asset(static_asset: &'static [u8]) -> Cow<'static, [u
88
}
99

1010
pub mod icons {
11+
12+
pub static BINOCULARS_FILL: &[u8; 639] =
13+
include_bytes!("../../assets/icons/binoculars-fill.svg");
14+
pub static CARD_CHECKLIST: &[u8; 730] = include_bytes!("../../assets/icons/card-checklist.svg");
15+
pub static FILM: &[u8; 384] = include_bytes!("../../assets/icons/film.svg");
16+
pub static GRAPH_UP_ARROW: &[u8; 402] = include_bytes!("../../assets/icons/graph-up-arrow.svg");
17+
pub static GEAR_WIDE_CONNECTED: &[u8; 1312] =
18+
include_bytes!("../../assets/icons/gear-wide-connected.svg");
19+
1120
pub static ARROW_BAR_UP: &[u8; 376] = include_bytes!("../../assets/icons/arrow-bar-up.svg");
1221
pub static ARROW_BAR_DOWN: &[u8; 375] = include_bytes!("../../assets/icons/arrow-bar-down.svg");
1322
pub static ARROW_LEFT: &[u8; 311] = include_bytes!("../../assets/icons/arrow-left.svg");

src/gui/mod.rs

+14-20
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use view::watchlist_view::{Message as WatchlistMessage, WatchlistTab};
1515

1616
use iced::widget::{container, text, Column};
1717
use iced::{Application, Command, Element, Length};
18-
use iced_aw::{TabBarStyles, TabLabel, Tabs};
1918

2019
use super::core::settings_config;
2120

@@ -55,7 +54,7 @@ impl Into<usize> for TabId {
5554

5655
#[derive(Debug, Clone)]
5756
pub enum Message {
58-
TabSelected(TabId),
57+
TabSelected(usize),
5958
Discover(DiscoverMessage),
6059
Watchlist(WatchlistMessage),
6160
MyShows(MyShowsMessage),
@@ -127,6 +126,7 @@ impl Application for TroxideGui {
127126
match message {
128127
Message::TabSelected(tab_id) => {
129128
self.series_view_active = false;
129+
let tab_id: TabId = tab_id.into();
130130
self.active_tab = tab_id.clone();
131131

132132
if let TabId::MyShows = tab_id {
@@ -168,29 +168,27 @@ impl Application for TroxideGui {
168168
}
169169

170170
fn view(&self) -> iced::Element<'_, Message, iced::Renderer<Self::Theme>> {
171-
let mut tabs: Vec<(TabId, TabLabel, Element<'_, Message, iced::Renderer>)> = vec![
171+
let mut tabs: Vec<(
172+
troxide_widget::tabs::TabLabel,
173+
Element<'_, Message, iced::Renderer>,
174+
)> = vec![
172175
(
173-
TabId::Discover,
174176
self.discover_tab.tab_label(),
175177
self.discover_tab.view().map(Message::Discover),
176178
),
177179
(
178-
TabId::Watchlist,
179180
self.watchlist_tab.tab_label(),
180181
self.watchlist_tab.view().map(Message::Watchlist),
181182
),
182183
(
183-
TabId::MyShows,
184184
self.my_shows_tab.tab_label(),
185185
self.my_shows_tab.view().map(Message::MyShows),
186186
),
187187
(
188-
TabId::Statistics,
189188
self.statistics_tab.tab_label(),
190189
self.statistics_tab.view().map(Message::Statistics),
191190
),
192191
(
193-
TabId::Settings,
194192
self.settings_tab.tab_label(),
195193
self.settings_tab.view().map(Message::Settings),
196194
),
@@ -200,8 +198,10 @@ impl Application for TroxideGui {
200198

201199
// Hijacking the current tab view when series view is active
202200
if self.series_view_active {
203-
let (_, _, current_view): &mut (TabId, TabLabel, Element<'_, Message, iced::Renderer>) =
204-
&mut tabs[active_tab_index];
201+
let (_, current_view): &mut (
202+
troxide_widget::tabs::TabLabel,
203+
Element<'_, Message, iced::Renderer>,
204+
) = &mut tabs[active_tab_index];
205205
*current_view = self
206206
.series_view
207207
.as_ref()
@@ -210,15 +210,9 @@ impl Application for TroxideGui {
210210
.map(Message::Series);
211211
}
212212

213-
let tab_bar_theme = match self.settings_tab.get_config_settings().theme {
214-
settings_config::Theme::Light => TabBarStyles::Default,
215-
settings_config::Theme::Dark => TabBarStyles::Dark,
216-
};
217-
218-
Tabs::with_tabs(tabs, Message::TabSelected)
219-
.set_active_tab(&self.active_tab)
220-
.tab_bar_style(tab_bar_theme)
221-
.into()
213+
troxide_widget::tabs::Tabs::with_tabs(tabs, Message::TabSelected)
214+
.set_active_tab(active_tab_index)
215+
.view()
222216
}
223217
}
224218

@@ -281,7 +275,7 @@ trait Tab {
281275

282276
fn title(&self) -> String;
283277

284-
fn tab_label(&self) -> TabLabel;
278+
fn tab_label(&self) -> troxide_widget::tabs::TabLabel;
285279

286280
fn view(&self) -> Element<'_, Self::Message> {
287281
let column = Column::new()

src/gui/styles/container_styles.rs

+68
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ pub fn release_time_container_theme() -> Container {
2222
)
2323
}
2424

25+
/// A custom theme for container respecting Light and Dark TroxideTheme designed for the tabs
26+
pub fn first_class_container_tab_theme() -> Container {
27+
Container::Custom(Box::new(ContainerTabThemeFirst) as Box<dyn StyleSheet<Style = iced::Theme>>)
28+
}
29+
30+
/// A custom theme for container respecting Light and Dark TroxideTheme designed for the tabs
31+
pub fn second_class_container_tab_theme() -> Container {
32+
Container::Custom(Box::new(ContainerTabThemeSecond) as Box<dyn StyleSheet<Style = iced::Theme>>)
33+
}
34+
2535
pub struct ContainerThemeFirst;
2636

2737
impl StyleSheet for ContainerThemeFirst {
@@ -80,6 +90,64 @@ impl StyleSheet for ContainerThemeSecond {
8090
}
8191
}
8292

93+
pub struct ContainerTabThemeFirst;
94+
95+
impl StyleSheet for ContainerTabThemeFirst {
96+
type Style = iced::Theme;
97+
98+
fn appearance(&self, style: &Self::Style) -> Appearance {
99+
let mut appearance = Appearance {
100+
// border_width: 1.0,
101+
// border_radius: 10.0,
102+
..Appearance::default()
103+
};
104+
105+
match style {
106+
iced::Theme::Custom(custom) => {
107+
if **custom == TroxideTheme::get_theme(&TroxideTheme::Light) {
108+
appearance.background = Some(Background::Color(color!(0xcccccc)));
109+
appearance.border_color = color!(0xbbbbbb);
110+
appearance
111+
} else {
112+
appearance.background = Some(Background::Color(color!(0x1c1c1c)));
113+
appearance.border_color = Color::BLACK;
114+
appearance
115+
}
116+
}
117+
_ => unreachable!("built-in iced themes are not in use"),
118+
}
119+
}
120+
}
121+
122+
pub struct ContainerTabThemeSecond;
123+
124+
impl StyleSheet for ContainerTabThemeSecond {
125+
type Style = iced::Theme;
126+
127+
fn appearance(&self, style: &Self::Style) -> Appearance {
128+
let mut appearance = Appearance {
129+
// border_width: 1.0,
130+
// border_radius: 10.0,
131+
..Appearance::default()
132+
};
133+
134+
match style {
135+
iced::Theme::Custom(custom) => {
136+
if **custom == TroxideTheme::get_theme(&TroxideTheme::Light) {
137+
appearance.background = Some(Background::Color(color!(0xbbbbbb)));
138+
appearance.border_color = color!(0xbbbbbb);
139+
appearance
140+
} else {
141+
appearance.background = Some(Background::Color(color!(0x282828)));
142+
appearance.border_color = Color::BLACK;
143+
appearance
144+
}
145+
}
146+
_ => unreachable!("built-in iced themes are not in use"),
147+
}
148+
}
149+
}
150+
83151
pub struct ContainerThemeReleaseTime;
84152

85153
impl StyleSheet for ContainerThemeReleaseTime {

src/gui/troxide_widget.rs

+94
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,97 @@ pub mod series_poster {
294294
}
295295
}
296296
}
297+
298+
pub mod tabs {
299+
use iced::widget::{column, container, horizontal_space, mouse_area, row, svg, text, Row};
300+
use iced::{Element, Length, Renderer};
301+
302+
use crate::gui::assets::get_static_cow_from_asset;
303+
use crate::gui::styles;
304+
305+
pub struct TabLabel {
306+
pub text: String,
307+
pub icon: &'static [u8],
308+
}
309+
310+
impl TabLabel {
311+
pub fn new(text: String, icon: &'static [u8]) -> Self {
312+
Self { text, icon }
313+
}
314+
}
315+
316+
pub struct Tabs<'a, Message> {
317+
active_tab: usize,
318+
on_select: Box<dyn Fn(usize) -> Message>,
319+
tabs: Vec<(TabLabel, Element<'a, Message, Renderer>)>,
320+
}
321+
322+
impl<'a, Message> Tabs<'a, Message>
323+
where
324+
Message: Clone + 'a,
325+
{
326+
pub fn with_tabs<F>(
327+
tabs: Vec<(TabLabel, Element<'a, Message, Renderer>)>,
328+
on_select: F,
329+
) -> Self
330+
where
331+
F: 'static + Fn(usize) -> Message,
332+
{
333+
Self {
334+
active_tab: usize::default(),
335+
on_select: Box::new(on_select),
336+
tabs,
337+
}
338+
}
339+
340+
pub fn set_active_tab(mut self, tab_id: usize) -> Self {
341+
self.active_tab = tab_id;
342+
self
343+
}
344+
345+
fn tab_view(&self) -> iced::Element<'a, Message, Renderer> {
346+
let tab_views = self
347+
.tabs
348+
.iter()
349+
.enumerate()
350+
.map(|(index, (tab_label, _))| {
351+
let svg_handle =
352+
svg::Handle::from_memory(get_static_cow_from_asset(tab_label.icon));
353+
let icon = svg(svg_handle)
354+
.width(Length::Shrink)
355+
.style(styles::svg_styles::colored_svg_theme());
356+
let text_label = text(&tab_label.text);
357+
let mut tab = container(
358+
mouse_area(row![icon, text_label].spacing(5))
359+
.on_press((self.on_select)(index)),
360+
)
361+
.padding(5);
362+
363+
// Highlighting the tab if is active
364+
if index == self.active_tab {
365+
tab =
366+
tab.style(styles::container_styles::second_class_container_tab_theme())
367+
};
368+
tab.into()
369+
})
370+
.collect();
371+
372+
let tab_views = Row::with_children(tab_views).spacing(10);
373+
374+
container(row![
375+
horizontal_space(Length::Fill),
376+
tab_views,
377+
horizontal_space(Length::Fill)
378+
])
379+
.style(styles::container_styles::first_class_container_tab_theme())
380+
.into()
381+
}
382+
383+
pub fn view(mut self) -> Element<'a, Message, Renderer> {
384+
let tab_view = self.tab_view();
385+
let active_tab = self.active_tab;
386+
let main_view = self.tabs.swap_remove(active_tab).1;
387+
column![tab_view, main_view].into()
388+
}
389+
}
390+
}

src/gui/view/discover_view.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ use crate::core::api::episodes_information::Episode;
22
use crate::core::api::series_information::SeriesMainInformation;
33
use crate::core::api::tv_schedule::{get_episodes_with_country, get_episodes_with_date};
44
use crate::core::api::updates::show_updates::*;
5+
use crate::gui::assets::icons::BINOCULARS_FILL;
56
use crate::gui::troxide_widget::series_poster::{Message as SeriesPosterMessage, SeriesPoster};
6-
use crate::gui::{Message as GuiMessage, Tab};
7+
use crate::gui::{troxide_widget, Message as GuiMessage, Tab};
78
use searching::Message as SearchMessage;
89

910
use iced::widget::{column, container, scrollable, text, vertical_space};
@@ -205,8 +206,8 @@ impl Tab for DiscoverTab {
205206
"Discover".to_owned()
206207
}
207208

208-
fn tab_label(&self) -> iced_aw::TabLabel {
209-
iced_aw::TabLabel::Text(self.title())
209+
fn tab_label(&self) -> troxide_widget::tabs::TabLabel {
210+
troxide_widget::tabs::TabLabel::new(self.title(), BINOCULARS_FILL)
210211
}
211212

212213
fn content(&self) -> Element<'_, Self::Message> {

src/gui/view/my_shows_view.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use crate::core::api::episodes_information::Episode;
44
use crate::core::caching;
55
use crate::core::caching::episode_list::EpisodeReleaseTime;
66
use crate::core::{api::series_information::SeriesMainInformation, database};
7+
use crate::gui::assets::icons::FILM;
78
use crate::gui::troxide_widget::series_poster::{Message as SeriesPosterMessage, SeriesPoster};
8-
use crate::gui::troxide_widget::{GREEN_THEME, RED_THEME};
9+
use crate::gui::troxide_widget::{self, GREEN_THEME, RED_THEME};
910
use crate::gui::{Message as GuiMessage, Tab};
1011
use iced::widget::{container, scrollable, Column};
1112
use iced_aw::{Spinner, Wrap};
@@ -377,8 +378,8 @@ impl Tab for MyShowsTab {
377378
"My Shows".to_owned()
378379
}
379380

380-
fn tab_label(&self) -> iced_aw::TabLabel {
381-
iced_aw::TabLabel::Text(self.title())
381+
fn tab_label(&self) -> troxide_widget::tabs::TabLabel {
382+
troxide_widget::tabs::TabLabel::new(self.title(), FILM)
382383
}
383384

384385
fn content(&self) -> Element<'_, Self::Message> {

src/gui/view/settings_view.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use iced::widget::{button, column, horizontal_space, pick_list, row, text, verti
22
use iced::{Element, Length, Renderer};
33

44
use crate::core::settings_config::{save_config, Config, Theme, ALL_THEMES};
5-
use crate::gui::{Message as GuiMessage, Tab};
5+
use crate::gui::assets::icons::GEAR_WIDE_CONNECTED;
6+
use crate::gui::{troxide_widget, Message as GuiMessage, Tab};
67

78
#[derive(Debug, Clone)]
89
pub enum Message {
@@ -87,8 +88,8 @@ impl Tab for SettingsTab {
8788
"Settings".to_owned()
8889
}
8990

90-
fn tab_label(&self) -> iced_aw::TabLabel {
91-
iced_aw::TabLabel::Text(self.title())
91+
fn tab_label(&self) -> troxide_widget::tabs::TabLabel {
92+
troxide_widget::tabs::TabLabel::new(self.title(), GEAR_WIDE_CONNECTED)
9293
}
9394

9495
fn content(&self) -> Element<'_, Self::Message> {

0 commit comments

Comments
 (0)