use toolbar in columns rather than chrome

Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
kernelkind
2025-08-13 19:11:02 -04:00
parent 49ef85aef6
commit b750c0a927
4 changed files with 52 additions and 353 deletions

View File

@@ -12,76 +12,31 @@ use notedeck::{
tr, App, AppAction, AppContext, Localization, Notedeck, NotedeckOptions, NotedeckTextStyle,
UserAccount, WalletType,
};
use notedeck_columns::{
column::SelectionResult,
timeline::{kind::ListKind, TimelineKind},
Damus,
};
use notedeck_columns::{timeline::TimelineKind, Damus};
use notedeck_dave::{Dave, DaveAvatar};
use notedeck_ui::{app_images, AnimationHelper, ProfilePic};
use notedeck_ui::{
app_images, expanding_button, AnimationHelper, ProfilePic, ICON_EXPANSION_MULTIPLE, ICON_WIDTH,
};
use std::collections::HashMap;
static ICON_WIDTH: f32 = 40.0;
pub static ICON_EXPANSION_MULTIPLE: f32 = 1.2;
#[derive(Default)]
pub struct Chrome {
active: i32,
tab_selected: i32,
options: ChromeOptions,
apps: Vec<NotedeckApp>,
pub repaint_causes: HashMap<egui::RepaintCause, u64>,
}
/// When you click the toolbar button, these actions
/// are returned
#[derive(Debug, Eq, PartialEq)]
pub enum ToolbarAction {
Notifications,
Dave,
Home,
}
pub enum ChromePanelAction {
Support,
Settings,
Account,
Wallet,
Toolbar(ToolbarAction),
SaveTheme(ThemePreference),
Profile(notedeck::enostr::Pubkey),
}
impl ChromePanelAction {
fn columns_switch(ctx: &mut AppContext, chrome: &mut Chrome, kind: &TimelineKind) {
chrome.switch_to_columns();
let Some(columns_app) = chrome.get_columns_app() else {
return;
};
if let Some(active_columns) = columns_app
.decks_cache
.active_columns_mut(ctx.i18n, ctx.accounts)
{
match active_columns.select_by_kind(kind) {
SelectionResult::NewSelection(_index) => {
// great! no need to go to top yet
}
SelectionResult::AlreadySelected(_n) => {
// we already selected this, so scroll to top
columns_app.scroll_to_top();
}
SelectionResult::Failed => {
// oh no, something went wrong
// TODO(jb55): handle tab selection failure
}
}
}
}
fn columns_navigate(ctx: &mut AppContext, chrome: &mut Chrome, route: notedeck_columns::Route) {
chrome.switch_to_columns();
@@ -107,30 +62,6 @@ impl ChromePanelAction {
ctx.settings.set_theme(*theme);
}
Self::Toolbar(toolbar_action) => match toolbar_action {
ToolbarAction::Dave => chrome.switch_to_dave(),
ToolbarAction::Home => {
Self::columns_switch(
ctx,
chrome,
&TimelineKind::List(ListKind::Contact(
ctx.accounts.get_selected_account().key.pubkey,
)),
);
}
ToolbarAction::Notifications => {
Self::columns_switch(
ctx,
chrome,
&TimelineKind::Notifications(
ctx.accounts.get_selected_account().key.pubkey,
),
);
}
},
Self::Support => {
Self::columns_navigate(ctx, chrome, notedeck_columns::Route::Support);
}
@@ -224,24 +155,6 @@ impl Chrome {
None
}
fn get_dave(&mut self) -> Option<&mut Dave> {
for app in &mut self.apps {
if let NotedeckApp::Dave(dave) = app {
return Some(dave);
}
}
None
}
fn switch_to_dave(&mut self) {
for (i, app) in self.apps.iter().enumerate() {
if let NotedeckApp::Dave(_) = app {
self.active = i as i32;
}
}
}
fn switch_to_columns(&mut self) {
for (i, app) in self.apps.iter().enumerate() {
if let NotedeckApp::Columns(_) = app {
@@ -337,106 +250,6 @@ impl Chrome {
* side_panel_width
}
fn toolbar_height() -> f32 {
48.0
}
/// On narrow layouts, we have a toolbar
fn toolbar_chrome(
&mut self,
ctx: &mut AppContext,
ui: &mut egui::Ui,
) -> Option<ChromePanelAction> {
let mut got_action: Option<ChromePanelAction> = None;
let amt_open = self.amount_open(ui);
StripBuilder::new(ui)
.size(Size::remainder()) // top cell
.size(Size::exact(Self::toolbar_height())) // bottom cell
.vertical(|mut strip| {
strip.strip(|builder| {
// the chrome panel is nested above the toolbar
got_action = self.panel(ctx, builder, amt_open);
});
strip.cell(|ui| {
let pk = ctx.accounts.get_selected_account().key.pubkey;
let unseen_notification =
unseen_notification(self.get_columns_app(), ctx.ndb, pk);
if let Some(action) = self.toolbar(ui, unseen_notification) {
got_action = Some(ChromePanelAction::Toolbar(action))
}
});
});
got_action
}
fn toolbar(&mut self, ui: &mut egui::Ui, unseen_notification: bool) -> Option<ToolbarAction> {
use egui_tabs::{TabColor, Tabs};
let rect = ui.available_rect_before_wrap();
ui.painter().hline(
rect.x_range(),
rect.top(),
ui.visuals().widgets.noninteractive.bg_stroke,
);
if !ui.visuals().dark_mode {
ui.painter().rect(
rect,
0,
notedeck_ui::colors::ALMOST_WHITE,
egui::Stroke::new(0.0, Color32::TRANSPARENT),
egui::StrokeKind::Inside,
);
}
let rs = Tabs::new(3)
.selected(self.tab_selected)
.hover_bg(TabColor::none())
.selected_fg(TabColor::none())
.selected_bg(TabColor::none())
.height(Self::toolbar_height())
.layout(Layout::centered_and_justified(egui::Direction::TopDown))
.show(ui, |ui, state| {
let index = state.index();
let mut action: Option<ToolbarAction> = None;
let btn_size: f32 = 20.0;
if index == 0 {
if home_button(ui, btn_size).clicked() {
action = Some(ToolbarAction::Home);
}
} else if index == 1 {
if let Some(dave) = self.get_dave() {
let rect = dave_toolbar_rect(ui, btn_size * 2.0);
if dave_button(dave.avatar_mut(), ui, rect).clicked() {
action = Some(ToolbarAction::Dave);
}
}
} else if index == 2
&& notifications_button(ui, btn_size, unseen_notification).clicked()
{
action = Some(ToolbarAction::Notifications);
}
action
})
.inner();
for maybe_r in rs {
if maybe_r.inner.is_some() {
return maybe_r.inner;
}
}
None
}
/// Show the side menu or bar, depending on if we're on a narrow
/// or wide screen.
///
@@ -451,12 +264,8 @@ impl Chrome {
self.options.toggle(ChromeOptions::VirtualKeyboard);
}
let r = if notedeck::ui::is_narrow(ui.ctx()) {
self.toolbar_chrome(ctx, ui)
} else {
let amt_open = self.amount_open(ui);
self.panel(ctx, StripBuilder::new(ui), amt_open)
};
let amt_open = self.amount_open(ui);
let r = self.panel(ctx, StripBuilder::new(ui), amt_open);
// virtual keyboard
if self.options.contains(ChromeOptions::VirtualKeyboard) {
@@ -514,38 +323,6 @@ impl Chrome {
}
}
fn unseen_notification(
columns: Option<&mut Damus>,
ndb: &nostrdb::Ndb,
current_pk: notedeck::enostr::Pubkey,
) -> bool {
let Some(columns) = columns else {
return false;
};
let Some(tl) = columns
.timeline_cache
.get_mut(&TimelineKind::Notifications(current_pk))
else {
return false;
};
let freshness = &mut tl.current_view_mut().freshness;
freshness.update(|timestamp_last_viewed| {
let filter = notedeck_columns::timeline::kind::notifications_filter(&current_pk)
.since_mut(timestamp_last_viewed);
let txn = Transaction::new(ndb).expect("txn");
let Some(res) = ndb.query(&txn, &[filter], 1).ok() else {
return false;
};
!res.is_empty()
});
freshness.has_unseen()
}
impl notedeck::App for Chrome {
fn update(&mut self, ctx: &mut notedeck::AppContext, ui: &mut egui::Ui) -> Option<AppAction> {
if let Some(action) = self.show(ctx, ui) {
@@ -593,52 +370,6 @@ fn expand_side_panel_button() -> impl Widget {
}
}
fn expanding_button(
name: &'static str,
img_size: f32,
light_img: egui::Image,
dark_img: egui::Image,
ui: &mut egui::Ui,
unseen_indicator: bool,
) -> egui::Response {
let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
let img = if ui.visuals().dark_mode {
dark_img
} else {
light_img
};
let helper = AnimationHelper::new(ui, name, egui::vec2(max_size, max_size));
let cur_img_size = helper.scale_1d_pos(img_size);
let paint_rect = helper
.get_animation_rect()
.shrink((max_size - cur_img_size) / 2.0);
img.paint_at(ui, paint_rect);
if unseen_indicator {
paint_unseen_indicator(ui, paint_rect, helper.scale_1d_pos(3.0));
}
helper.take_animation_response()
}
fn paint_unseen_indicator(ui: &mut egui::Ui, rect: egui::Rect, radius: f32) {
let center = rect.center();
let top_right = rect.right_top();
let distance = center.distance(top_right);
let midpoint = {
let mut cur = center;
cur.x += distance / 2.0;
cur.y -= distance / 2.0;
cur
};
let painter = ui.painter_at(rect);
painter.circle_filled(midpoint, radius, notedeck_ui::colors::PINK);
}
fn support_button(ui: &mut egui::Ui) -> egui::Response {
expanding_button(
"help-button",
@@ -661,28 +392,6 @@ fn settings_button(ui: &mut egui::Ui) -> egui::Response {
)
}
fn notifications_button(ui: &mut egui::Ui, size: f32, unseen_indicator: bool) -> egui::Response {
expanding_button(
"notifications-button",
size,
app_images::notifications_light_image(),
app_images::notifications_dark_image(),
ui,
unseen_indicator,
)
}
fn home_button(ui: &mut egui::Ui, size: f32) -> egui::Response {
expanding_button(
"home-button",
size,
app_images::home_light_image(),
app_images::home_dark_image(),
ui,
false,
)
}
fn columns_button(ui: &mut egui::Ui) -> egui::Response {
expanding_button(
"columns-button",
@@ -735,14 +444,6 @@ fn dave_sidebar_rect(ui: &mut egui::Ui) -> Rect {
egui::Rect::from_center_size(egui::pos2(center_x, center_y), size)
}
fn dave_toolbar_rect(ui: &mut egui::Ui, size: f32) -> Rect {
let size = vec2(size, size);
let available = ui.available_rect_before_wrap();
let center_x = available.center().x;
let center_y = available.center().y;
egui::Rect::from_center_size(egui::pos2(center_x, center_y), size)
}
fn dave_button(avatar: Option<&mut DaveAvatar>, ui: &mut egui::Ui, rect: Rect) -> egui::Response {
if let Some(avatar) = avatar {
avatar.render(rect, ui)