mirror of
https://github.com/aljazceru/notedeck.git
synced 2026-01-04 00:44:20 +01:00
use toolbar in columns rather than chrome
Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
@@ -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(¤t_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)
|
||||
|
||||
Reference in New Issue
Block a user