Skip to content

Commit 9e8f7a2

Browse files
committed
ui: integrate egui-tabs for notes & replies selector
demo: https://cdn.jb55.com/s/notedeck-tabs.mp4 Fixes: #47 Signed-off-by: William Casarin <[email protected]>
1 parent 0298966 commit 9e8f7a2

File tree

6 files changed

+108
-10
lines changed

6 files changed

+108
-10
lines changed

Cargo.lock

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ eframe = { version = "0.27.2", default-features = false, features = [ "glow", "w
1919
#eframe = "0.22.0"
2020
egui_extras = { version = "0.27.2", features = ["all_loaders"] }
2121
ehttp = "0.2.0"
22+
egui-tabs = { git = "https://github.com/damus-io/egui-tabs", rev = "ed97a57fc66b3781bc10ab644f9e1ed125d7377a" }
2223
reqwest = { version = "0.12.4", default-features = false, features = [ "rustls-tls-native-roots" ] }
2324
image = { version = "0.24", features = ["jpeg", "png", "webp"] }
2425
log = "0.4.17"

src/app.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ fn relay_setup(pool: &mut RelayPool, ctx: &egui::Context) {
5656
if let Err(e) = pool.add_url("wss://relay.damus.io".to_string(), wakeup.clone()) {
5757
error!("{:?}", e)
5858
}
59-
if let Err(e) = pool.add_url("wss://pyramid.fiatjaf.com".to_string(), wakeup.clone()) {
60-
error!("{:?}", e)
61-
}
59+
//if let Err(e) = pool.add_url("wss://pyramid.fiatjaf.com".to_string(), wakeup.clone()) {
60+
//error!("{:?}", e)
61+
//}
6262
if let Err(e) = pool.add_url("wss://nos.lol".to_string(), wakeup.clone()) {
6363
error!("{:?}", e)
6464
}

src/app_creation.rs

+4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ pub fn setup_cc(cc: &eframe::CreationContext<'_>) {
4141
setup_fonts(ctx);
4242

4343
//ctx.set_pixels_per_point(ctx.pixels_per_point() + UI_SCALE_FACTOR);
44+
//ctx.set_pixels_per_point(1.0);
45+
//
46+
//
47+
//ctx.tessellation_options_mut(|to| to.feathering = false);
4448

4549
egui_extras::install_image_loaders(ctx);
4650

src/colors.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const DARK_BG: Color32 = Color32::from_rgb(0x2C, 0x2C, 0x2C);
1515
const DARK_ISH_BG: Color32 = Color32::from_rgb(0x22, 0x22, 0x22);
1616
const SEMI_DARK_BG: Color32 = Color32::from_rgb(0x44, 0x44, 0x44);
1717

18+
const LIGHTER_GRAY: Color32 = Color32::from_rgb(0xe8, 0xe8, 0xe8);
1819
const LIGHT_GRAY: Color32 = Color32::from_rgb(0xc8, 0xc8, 0xc8); // 78%
1920
pub const MID_GRAY: Color32 = Color32::from_rgb(0xbd, 0xbd, 0xbd);
2021
const DARKER_GRAY: Color32 = Color32::from_rgb(0xa5, 0xa5, 0xa5); // 65%
@@ -99,7 +100,7 @@ pub fn light_color_theme() -> ColorTheme {
99100
// NONINTERACTIVE WIDGET
100101
noninteractive_bg_fill: Color32::WHITE,
101102
noninteractive_weak_bg_fill: EVEN_DARKER_GRAY,
102-
noninteractive_bg_stroke_color: DARKER_GRAY,
103+
noninteractive_bg_stroke_color: LIGHTER_GRAY,
103104
noninteractive_fg_stroke_color: GRAY_SECONDARY,
104105

105106
// INACTIVE WIDGET

src/timeline.rs

+88-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::{ui, Damus};
22
use egui::containers::scroll_area::ScrollBarVisibility;
3+
use egui::{Direction, Layout};
4+
use egui_tabs::TabColor;
35
use egui_virtual_list::VirtualList;
46
use enostr::Filter;
57
use nostrdb::{NoteKey, Subscription, Transaction};
@@ -56,21 +58,101 @@ impl Timeline {
5658
}
5759
}
5860

61+
fn get_label_width(ui: &mut egui::Ui, text: &str) -> f32 {
62+
let font_id = egui::FontId::default();
63+
let galley = ui.fonts(|r| r.layout_no_wrap(text.to_string(), font_id, egui::Color32::WHITE));
64+
galley.rect.width()
65+
}
66+
67+
fn shrink_range_to_width(range: egui::Rangef, width: f32) -> egui::Rangef {
68+
let midpoint = (range.min + range.max) / 2.0;
69+
let half_width = width / 2.0;
70+
71+
let min = midpoint - half_width;
72+
let max = midpoint + half_width;
73+
74+
egui::Rangef::new(min, max)
75+
}
76+
77+
fn tabs_ui(ui: &mut egui::Ui) {
78+
ui.spacing_mut().item_spacing.y = 0.0;
79+
80+
let tab_res = egui_tabs::Tabs::new(2)
81+
.hover_bg(TabColor::none())
82+
.selected_fg(TabColor::none())
83+
.selected_bg(TabColor::none())
84+
.hover_bg(TabColor::none())
85+
//.hover_bg(TabColor::custom(egui::Color32::RED))
86+
.height(32.0)
87+
.layout(Layout::centered_and_justified(Direction::TopDown))
88+
.show(ui, |ui, state| {
89+
ui.spacing_mut().item_spacing.y = 0.0;
90+
91+
let ind = state.index();
92+
93+
let txt = if ind == 0 { "Notes" } else { "Notes & Replies" };
94+
95+
let res = ui.add(egui::Label::new(txt).selectable(false));
96+
97+
// underline
98+
if state.is_selected() {
99+
let rect = res.rect;
100+
let underline = rect.x_range().shrink(rect.width() / 4.0);
101+
let underline = shrink_range_to_width(underline, get_label_width(ui, txt) * 1.15);
102+
let underline_y = ui.painter().round_to_pixel(rect.bottom()) - 1.5;
103+
return (underline, underline_y);
104+
}
105+
106+
(egui::Rangef::new(0.0, 0.0), 0.0)
107+
});
108+
109+
//ui.add_space(0.5);
110+
ui::hline(ui);
111+
112+
// fun animation
113+
if let Some(sel) = tab_res.selected() {
114+
let (underline, underline_y) = tab_res.inner()[sel as usize].inner;
115+
let underline_width = underline.span();
116+
117+
let tab_anim_id = ui.id().with("tab_anim");
118+
let tab_anim_size = tab_anim_id.with("size");
119+
120+
let stroke = egui::Stroke {
121+
color: ui.visuals().hyperlink_color,
122+
width: 3.0,
123+
};
124+
125+
let speed = 0.1f32;
126+
127+
// animate underline position
128+
let x = ui
129+
.ctx()
130+
.animate_value_with_time(tab_anim_id, underline.min, speed);
131+
132+
// animate underline width
133+
let w = ui
134+
.ctx()
135+
.animate_value_with_time(tab_anim_size, underline_width, speed);
136+
137+
let underline = egui::Rangef::new(x, x + w);
138+
139+
ui.painter().hline(underline, underline_y, stroke);
140+
}
141+
142+
ui.add_space(3.0);
143+
}
144+
59145
pub fn timeline_view(ui: &mut egui::Ui, app: &mut Damus, timeline: usize) {
60146
//padding(4.0, ui, |ui| ui.heading("Notifications"));
61147
/*
62148
let font_id = egui::TextStyle::Body.resolve(ui.style());
63149
let row_height = ui.fonts(|f| f.row_height(&font_id)) + ui.spacing().item_spacing.y;
64150
*/
65151

152+
tabs_ui(ui);
153+
66154
egui::ScrollArea::vertical()
67155
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible)
68-
//.auto_shrink([false; 2])
69-
/*
70-
.show_viewport(ui, |ui, viewport| {
71-
render_notes_in_viewport(ui, app, viewport, row_height, font_id);
72-
});
73-
*/
74156
.show(ui, |ui| {
75157
let len = app.timelines[timeline].notes.len();
76158
let list = app.timelines[timeline].list.clone();

0 commit comments

Comments
 (0)