ui: integrate egui-tabs for notes & replies selector

demo: https://cdn.jb55.com/s/notedeck-tabs.mp4

Fixes: https://github.com/damus-io/notedeck/issues/47
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin 2024-05-15 17:20:25 -07:00
parent 029896627c
commit 9e8f7a2e5c
6 changed files with 108 additions and 10 deletions

10
Cargo.lock generated
View File

@ -1035,6 +1035,15 @@ dependencies = [
"serde",
]
[[package]]
name = "egui-tabs"
version = "0.1.0"
source = "git+https://github.com/damus-io/egui-tabs?rev=ed97a57fc66b3781bc10ab644f9e1ed125d7377a#ed97a57fc66b3781bc10ab644f9e1ed125d7377a"
dependencies = [
"egui",
"egui_extras",
]
[[package]]
name = "egui-wgpu"
version = "0.27.2"
@ -2445,6 +2454,7 @@ dependencies = [
"console_error_panic_hook",
"eframe",
"egui",
"egui-tabs",
"egui_extras",
"egui_virtual_list",
"ehttp 0.2.0",

View File

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

View File

@ -56,9 +56,9 @@ fn relay_setup(pool: &mut RelayPool, ctx: &egui::Context) {
if let Err(e) = pool.add_url("wss://relay.damus.io".to_string(), wakeup.clone()) {
error!("{:?}", e)
}
if let Err(e) = pool.add_url("wss://pyramid.fiatjaf.com".to_string(), wakeup.clone()) {
error!("{:?}", e)
}
//if let Err(e) = pool.add_url("wss://pyramid.fiatjaf.com".to_string(), wakeup.clone()) {
//error!("{:?}", e)
//}
if let Err(e) = pool.add_url("wss://nos.lol".to_string(), wakeup.clone()) {
error!("{:?}", e)
}

View File

@ -41,6 +41,10 @@ pub fn setup_cc(cc: &eframe::CreationContext<'_>) {
setup_fonts(ctx);
//ctx.set_pixels_per_point(ctx.pixels_per_point() + UI_SCALE_FACTOR);
//ctx.set_pixels_per_point(1.0);
//
//
//ctx.tessellation_options_mut(|to| to.feathering = false);
egui_extras::install_image_loaders(ctx);

View File

@ -15,6 +15,7 @@ const DARK_BG: Color32 = Color32::from_rgb(0x2C, 0x2C, 0x2C);
const DARK_ISH_BG: Color32 = Color32::from_rgb(0x22, 0x22, 0x22);
const SEMI_DARK_BG: Color32 = Color32::from_rgb(0x44, 0x44, 0x44);
const LIGHTER_GRAY: Color32 = Color32::from_rgb(0xe8, 0xe8, 0xe8);
const LIGHT_GRAY: Color32 = Color32::from_rgb(0xc8, 0xc8, 0xc8); // 78%
pub const MID_GRAY: Color32 = Color32::from_rgb(0xbd, 0xbd, 0xbd);
const DARKER_GRAY: Color32 = Color32::from_rgb(0xa5, 0xa5, 0xa5); // 65%
@ -99,7 +100,7 @@ pub fn light_color_theme() -> ColorTheme {
// NONINTERACTIVE WIDGET
noninteractive_bg_fill: Color32::WHITE,
noninteractive_weak_bg_fill: EVEN_DARKER_GRAY,
noninteractive_bg_stroke_color: DARKER_GRAY,
noninteractive_bg_stroke_color: LIGHTER_GRAY,
noninteractive_fg_stroke_color: GRAY_SECONDARY,
// INACTIVE WIDGET

View File

@ -1,5 +1,7 @@
use crate::{ui, Damus};
use egui::containers::scroll_area::ScrollBarVisibility;
use egui::{Direction, Layout};
use egui_tabs::TabColor;
use egui_virtual_list::VirtualList;
use enostr::Filter;
use nostrdb::{NoteKey, Subscription, Transaction};
@ -56,6 +58,90 @@ impl Timeline {
}
}
fn get_label_width(ui: &mut egui::Ui, text: &str) -> f32 {
let font_id = egui::FontId::default();
let galley = ui.fonts(|r| r.layout_no_wrap(text.to_string(), font_id, egui::Color32::WHITE));
galley.rect.width()
}
fn shrink_range_to_width(range: egui::Rangef, width: f32) -> egui::Rangef {
let midpoint = (range.min + range.max) / 2.0;
let half_width = width / 2.0;
let min = midpoint - half_width;
let max = midpoint + half_width;
egui::Rangef::new(min, max)
}
fn tabs_ui(ui: &mut egui::Ui) {
ui.spacing_mut().item_spacing.y = 0.0;
let tab_res = egui_tabs::Tabs::new(2)
.hover_bg(TabColor::none())
.selected_fg(TabColor::none())
.selected_bg(TabColor::none())
.hover_bg(TabColor::none())
//.hover_bg(TabColor::custom(egui::Color32::RED))
.height(32.0)
.layout(Layout::centered_and_justified(Direction::TopDown))
.show(ui, |ui, state| {
ui.spacing_mut().item_spacing.y = 0.0;
let ind = state.index();
let txt = if ind == 0 { "Notes" } else { "Notes & Replies" };
let res = ui.add(egui::Label::new(txt).selectable(false));
// underline
if state.is_selected() {
let rect = res.rect;
let underline = rect.x_range().shrink(rect.width() / 4.0);
let underline = shrink_range_to_width(underline, get_label_width(ui, txt) * 1.15);
let underline_y = ui.painter().round_to_pixel(rect.bottom()) - 1.5;
return (underline, underline_y);
}
(egui::Rangef::new(0.0, 0.0), 0.0)
});
//ui.add_space(0.5);
ui::hline(ui);
// fun animation
if let Some(sel) = tab_res.selected() {
let (underline, underline_y) = tab_res.inner()[sel as usize].inner;
let underline_width = underline.span();
let tab_anim_id = ui.id().with("tab_anim");
let tab_anim_size = tab_anim_id.with("size");
let stroke = egui::Stroke {
color: ui.visuals().hyperlink_color,
width: 3.0,
};
let speed = 0.1f32;
// animate underline position
let x = ui
.ctx()
.animate_value_with_time(tab_anim_id, underline.min, speed);
// animate underline width
let w = ui
.ctx()
.animate_value_with_time(tab_anim_size, underline_width, speed);
let underline = egui::Rangef::new(x, x + w);
ui.painter().hline(underline, underline_y, stroke);
}
ui.add_space(3.0);
}
pub fn timeline_view(ui: &mut egui::Ui, app: &mut Damus, timeline: usize) {
//padding(4.0, ui, |ui| ui.heading("Notifications"));
/*
@ -63,14 +149,10 @@ pub fn timeline_view(ui: &mut egui::Ui, app: &mut Damus, timeline: usize) {
let row_height = ui.fonts(|f| f.row_height(&font_id)) + ui.spacing().item_spacing.y;
*/
tabs_ui(ui);
egui::ScrollArea::vertical()
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible)
//.auto_shrink([false; 2])
/*
.show_viewport(ui, |ui, viewport| {
render_notes_in_viewport(ui, app, viewport, row_height, font_id);
});
*/
.show(ui, |ui| {
let len = app.timelines[timeline].notes.len();
let list = app.timelines[timeline].list.clone();