mirror of
https://github.com/aljazceru/notedeck.git
synced 2026-02-05 08:34:19 +01:00
Merge drag to nav back on all views by kernel #1035
HE FUCKING DID IT LADS
Made a small tweak on the merge commit to update url to
damus-io/egui-nav upstream
William Casarin (2):
Merge drag to nav back on all views by kernel #1035
kernelkind (9):
TMP: update egui-nav
refactor scrolling for post, reply & quote views
enforce scroll_id for `ThreadView`
add `scroll_id` for all views with vertical scroll
add `DragSwitch`
use `DragSwitch` in `Column`
get scroll id for `Route`
add `route_uses_frame`
use `DragSwitch` to allow dragging anywhere in navigation
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1526,7 +1526,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "egui_nav"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/damus-io/egui-nav?rev=111de8ac40b5d18df53e9691eb18a50d49cb31d8#111de8ac40b5d18df53e9691eb18a50d49cb31d8"
|
||||
source = "git+https://github.com/kernelkind/egui-nav?rev=3c67eb6298edbff36d46546897cfac33df4f04db#3c67eb6298edbff36d46546897cfac33df4f04db"
|
||||
dependencies = [
|
||||
"egui",
|
||||
"egui_extras",
|
||||
|
||||
@@ -24,7 +24,7 @@ egui = { version = "0.31.1", features = ["serde"] }
|
||||
egui-wgpu = "0.31.1"
|
||||
egui_extras = { version = "0.31.1", features = ["all_loaders"] }
|
||||
egui-winit = { version = "0.31.1", features = ["android-game-activity", "clipboard"] }
|
||||
egui_nav = { git = "https://github.com/damus-io/egui-nav", rev = "111de8ac40b5d18df53e9691eb18a50d49cb31d8" }
|
||||
egui_nav = { git = "https://github.com/damus-io/egui-nav", rev = "3c67eb6298edbff36d46546897cfac33df4f04db" }
|
||||
egui_tabs = { git = "https://github.com/damus-io/egui-tabs", rev = "6eb91740577b374a8a6658c09c9a4181299734d0" }
|
||||
#egui_virtual_list = "0.6.0"
|
||||
egui_virtual_list = { git = "https://github.com/jb55/hello_egui", rev = "a66b6794f5e707a2f4109633770e02b02fb722e1" }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
actionbar::TimelineOpenResult,
|
||||
drag::DragSwitch,
|
||||
route::{Route, Router, SingletonRouter},
|
||||
timeline::{Timeline, TimelineCache, TimelineKind},
|
||||
};
|
||||
@@ -13,6 +14,7 @@ use tracing::warn;
|
||||
pub struct Column {
|
||||
pub router: Router<Route>,
|
||||
pub sheet_router: SingletonRouter<Route>,
|
||||
pub drag: DragSwitch,
|
||||
}
|
||||
|
||||
impl Column {
|
||||
@@ -21,6 +23,7 @@ impl Column {
|
||||
Column {
|
||||
router,
|
||||
sheet_router: SingletonRouter::default(),
|
||||
drag: DragSwitch::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
103
crates/notedeck_columns/src/drag.rs
Normal file
103
crates/notedeck_columns/src/drag.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct DragSwitch {
|
||||
state: Option<DragState>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct DragState {
|
||||
start_pos: egui::Pos2,
|
||||
cur_direction: DragDirection,
|
||||
}
|
||||
|
||||
impl DragSwitch {
|
||||
/// should call BEFORE both drag directions get rendered
|
||||
pub fn update(&mut self, horizontal: egui::Id, vertical: egui::Id, ctx: &egui::Context) {
|
||||
let horiz_being_dragged = ctx.is_being_dragged(horizontal);
|
||||
let vert_being_dragged = ctx.is_being_dragged(vertical);
|
||||
|
||||
if !horiz_being_dragged && !vert_being_dragged {
|
||||
self.state = None;
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(state) = &mut self.state else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(cur_pos) = ctx.pointer_interact_pos() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let dx = (state.start_pos.x - cur_pos.x).abs();
|
||||
let dy = (state.start_pos.y - cur_pos.y).abs();
|
||||
|
||||
let new_direction = if dx > dy {
|
||||
DragDirection::Horizontal
|
||||
} else {
|
||||
DragDirection::Vertical
|
||||
};
|
||||
|
||||
if new_direction == DragDirection::Horizontal
|
||||
&& state.cur_direction == DragDirection::Vertical
|
||||
{
|
||||
// drag is occuring mostly in the horizontal direction
|
||||
ctx.set_dragged_id(horizontal);
|
||||
let new_dir = DragDirection::Horizontal;
|
||||
state.cur_direction = new_dir;
|
||||
} else if new_direction == DragDirection::Vertical
|
||||
&& state.cur_direction == DragDirection::Horizontal
|
||||
{
|
||||
// drag is occuring mostly in the vertical direction
|
||||
let new_dir = DragDirection::Vertical;
|
||||
state.cur_direction = new_dir;
|
||||
ctx.set_dragged_id(vertical);
|
||||
}
|
||||
}
|
||||
|
||||
/// should call AFTER both drag directions rendered
|
||||
pub fn check_for_drag_start(
|
||||
&mut self,
|
||||
ctx: &egui::Context,
|
||||
horizontal: egui::Id,
|
||||
vertical: egui::Id,
|
||||
) {
|
||||
let Some(drag_id) = ctx.drag_started_id() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let cur_direction = if drag_id == horizontal {
|
||||
DragDirection::Horizontal
|
||||
} else if drag_id == vertical {
|
||||
DragDirection::Vertical
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(cur_pos) = ctx.pointer_interact_pos() else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.state = Some(DragState {
|
||||
start_pos: cur_pos,
|
||||
cur_direction,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum DragDirection {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
pub fn get_drag_id(ui: &egui::Ui, scroll_id: egui::Id) -> egui::Id {
|
||||
ui.id().with(egui::Id::new(scroll_id)).with("area")
|
||||
}
|
||||
|
||||
// unfortunately a Frame makes a new id for the Ui
|
||||
pub fn get_drag_id_through_frame(ui: &egui::Ui, scroll_id: egui::Id) -> egui::Id {
|
||||
ui.id()
|
||||
.with(egui::Id::new("child"))
|
||||
.with(egui::Id::new(scroll_id))
|
||||
.with("area")
|
||||
}
|
||||
@@ -12,6 +12,7 @@ pub mod column;
|
||||
mod deck_state;
|
||||
mod decks;
|
||||
mod draft;
|
||||
mod drag;
|
||||
mod key_parsing;
|
||||
pub mod login_manager;
|
||||
mod media_upload;
|
||||
|
||||
@@ -4,26 +4,28 @@ use crate::{
|
||||
column::ColumnsAction,
|
||||
deck_state::DeckState,
|
||||
decks::{Deck, DecksAction, DecksCache},
|
||||
drag::{get_drag_id, get_drag_id_through_frame},
|
||||
options::AppOptions,
|
||||
profile::{ProfileAction, SaveProfileChanges},
|
||||
route::{Route, Router, SingletonRouter},
|
||||
timeline::{
|
||||
route::{render_thread_route, render_timeline_route},
|
||||
TimelineCache,
|
||||
TimelineCache, TimelineKind,
|
||||
},
|
||||
ui::{
|
||||
self,
|
||||
add_column::render_add_column_routes,
|
||||
add_column::{render_add_column_routes, AddColumnView},
|
||||
column::NavTitle,
|
||||
configure_deck::ConfigureDeckView,
|
||||
edit_deck::{EditDeckResponse, EditDeckView},
|
||||
note::{custom_zap::CustomZapView, NewPostAction, PostAction, PostType},
|
||||
note::{custom_zap::CustomZapView, NewPostAction, PostAction, PostType, QuoteRepostView},
|
||||
profile::EditProfileView,
|
||||
search::{FocusState, SearchView},
|
||||
settings::{SettingsAction, ShowNoteClientOptions},
|
||||
support::SupportView,
|
||||
wallet::{get_default_zap_state, WalletAction, WalletState, WalletView},
|
||||
RelayView, SettingsView,
|
||||
AccountsView, PostReplyView, PostView, ProfileView, RelayView, SettingsView, ThreadView,
|
||||
TimelineView,
|
||||
},
|
||||
Damus,
|
||||
};
|
||||
@@ -631,27 +633,22 @@ fn render_nav_body(
|
||||
return None;
|
||||
};
|
||||
|
||||
let id = egui::Id::new(("post", col, note.key().unwrap()));
|
||||
let poster = ctx.accounts.selected_filled()?;
|
||||
|
||||
let action = {
|
||||
let draft = app.drafts.reply_mut(note.id());
|
||||
|
||||
let response = egui::ScrollArea::vertical()
|
||||
.show(ui, |ui| {
|
||||
ui::PostReplyView::new(
|
||||
&mut note_context,
|
||||
poster,
|
||||
draft,
|
||||
¬e,
|
||||
inner_rect,
|
||||
app.note_options,
|
||||
&mut app.jobs,
|
||||
)
|
||||
.id_source(id)
|
||||
.show(ui)
|
||||
})
|
||||
.inner;
|
||||
let response = ui::PostReplyView::new(
|
||||
&mut note_context,
|
||||
poster,
|
||||
draft,
|
||||
¬e,
|
||||
inner_rect,
|
||||
app.note_options,
|
||||
&mut app.jobs,
|
||||
col,
|
||||
)
|
||||
.show(ui);
|
||||
|
||||
response.action
|
||||
};
|
||||
@@ -672,26 +669,20 @@ fn render_nav_body(
|
||||
return None;
|
||||
};
|
||||
|
||||
let id = egui::Id::new(("post", col, note.key().unwrap()));
|
||||
|
||||
let poster = ctx.accounts.selected_filled()?;
|
||||
let draft = app.drafts.quote_mut(note.id());
|
||||
|
||||
let response = egui::ScrollArea::vertical()
|
||||
.show(ui, |ui| {
|
||||
crate::ui::note::QuoteRepostView::new(
|
||||
&mut note_context,
|
||||
poster,
|
||||
draft,
|
||||
¬e,
|
||||
inner_rect,
|
||||
app.note_options,
|
||||
&mut app.jobs,
|
||||
)
|
||||
.id_source(id)
|
||||
.show(ui)
|
||||
})
|
||||
.inner;
|
||||
let response = crate::ui::note::QuoteRepostView::new(
|
||||
&mut note_context,
|
||||
poster,
|
||||
draft,
|
||||
¬e,
|
||||
inner_rect,
|
||||
app.note_options,
|
||||
&mut app.jobs,
|
||||
col,
|
||||
)
|
||||
.show(ui);
|
||||
|
||||
response.action.map(Into::into)
|
||||
}
|
||||
@@ -964,47 +955,165 @@ pub fn render_nav(
|
||||
}
|
||||
};
|
||||
|
||||
let nav_response = Nav::new(
|
||||
&app.columns(ctx.accounts)
|
||||
.column(col)
|
||||
.router()
|
||||
.routes()
|
||||
.clone(),
|
||||
)
|
||||
.navigating(
|
||||
app.columns_mut(ctx.i18n, ctx.accounts)
|
||||
.column_mut(col)
|
||||
.router_mut()
|
||||
.navigating,
|
||||
)
|
||||
.returning(
|
||||
app.columns_mut(ctx.i18n, ctx.accounts)
|
||||
.column_mut(col)
|
||||
.router_mut()
|
||||
.returning,
|
||||
)
|
||||
.id_source(egui::Id::new(("nav", col)))
|
||||
.show_mut(ui, |ui, render_type, nav| match render_type {
|
||||
NavUiType::Title => NavTitle::new(
|
||||
ctx.ndb,
|
||||
ctx.img_cache,
|
||||
get_active_columns_mut(ctx.i18n, ctx.accounts, &mut app.decks_cache),
|
||||
nav.routes(),
|
||||
col,
|
||||
ctx.i18n,
|
||||
)
|
||||
.show_move_button(!narrow)
|
||||
.show_delete_button(!narrow)
|
||||
.show(ui),
|
||||
let routes = app
|
||||
.columns(ctx.accounts)
|
||||
.column(col)
|
||||
.router()
|
||||
.routes()
|
||||
.clone();
|
||||
let nav = Nav::new(&routes).id_source(egui::Id::new(("nav", col)));
|
||||
|
||||
NavUiType::Body => {
|
||||
if let Some(top) = nav.routes().last() {
|
||||
render_nav_body(ui, app, ctx, top, nav.routes().len(), col, inner_rect)
|
||||
} else {
|
||||
None
|
||||
let drag_ids = 's: {
|
||||
let Some(top_route) = &routes.last().cloned() else {
|
||||
break 's None;
|
||||
};
|
||||
|
||||
let Some(scroll_id) = get_scroll_id(
|
||||
top_route,
|
||||
app.columns(ctx.accounts)
|
||||
.column(col)
|
||||
.router()
|
||||
.routes()
|
||||
.len(),
|
||||
&app.timeline_cache,
|
||||
col,
|
||||
) else {
|
||||
break 's None;
|
||||
};
|
||||
|
||||
let vertical_drag_id = if route_uses_frame(top_route) {
|
||||
get_drag_id_through_frame(ui, scroll_id)
|
||||
} else {
|
||||
get_drag_id(ui, scroll_id)
|
||||
};
|
||||
|
||||
let horizontal_drag_id = nav.drag_id(ui);
|
||||
|
||||
let drag = &mut get_active_columns_mut(ctx.i18n, ctx.accounts, &mut app.decks_cache)
|
||||
.column_mut(col)
|
||||
.drag;
|
||||
|
||||
drag.update(horizontal_drag_id, vertical_drag_id, ui.ctx());
|
||||
|
||||
Some((horizontal_drag_id, vertical_drag_id))
|
||||
};
|
||||
|
||||
let nav_response = nav
|
||||
.navigating(
|
||||
app.columns_mut(ctx.i18n, ctx.accounts)
|
||||
.column_mut(col)
|
||||
.router_mut()
|
||||
.navigating,
|
||||
)
|
||||
.returning(
|
||||
app.columns_mut(ctx.i18n, ctx.accounts)
|
||||
.column_mut(col)
|
||||
.router_mut()
|
||||
.returning,
|
||||
)
|
||||
.show_mut(ui, |ui, render_type, nav| match render_type {
|
||||
NavUiType::Title => NavTitle::new(
|
||||
ctx.ndb,
|
||||
ctx.img_cache,
|
||||
get_active_columns_mut(ctx.i18n, ctx.accounts, &mut app.decks_cache),
|
||||
nav.routes(),
|
||||
col,
|
||||
ctx.i18n,
|
||||
)
|
||||
.show_move_button(!narrow)
|
||||
.show_delete_button(!narrow)
|
||||
.show(ui),
|
||||
|
||||
NavUiType::Body => {
|
||||
if let Some(top) = nav.routes().last() {
|
||||
render_nav_body(ui, app, ctx, top, nav.routes().len(), col, inner_rect)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if let Some((horizontal_drag_id, vertical_drag_id)) = drag_ids {
|
||||
let drag = &mut get_active_columns_mut(ctx.i18n, ctx.accounts, &mut app.decks_cache)
|
||||
.column_mut(col)
|
||||
.drag;
|
||||
drag.check_for_drag_start(ui.ctx(), horizontal_drag_id, vertical_drag_id);
|
||||
}
|
||||
|
||||
RenderNavResponse::new(col, NotedeckNavResponse::Nav(Box::new(nav_response)))
|
||||
}
|
||||
|
||||
fn get_scroll_id(
|
||||
top: &Route,
|
||||
depth: usize,
|
||||
timeline_cache: &TimelineCache,
|
||||
col: usize,
|
||||
) -> Option<egui::Id> {
|
||||
match top {
|
||||
Route::Timeline(timeline_kind) => match timeline_kind {
|
||||
TimelineKind::List(_)
|
||||
| TimelineKind::Search(_)
|
||||
| TimelineKind::Algo(_)
|
||||
| TimelineKind::Notifications(_)
|
||||
| TimelineKind::Universe
|
||||
| TimelineKind::Hashtag(_)
|
||||
| TimelineKind::Generic(_) => {
|
||||
TimelineView::scroll_id(timeline_cache, timeline_kind, col)
|
||||
}
|
||||
TimelineKind::Profile(pubkey) => {
|
||||
if depth > 1 {
|
||||
Some(ProfileView::scroll_id(col, pubkey))
|
||||
} else {
|
||||
TimelineView::scroll_id(timeline_cache, timeline_kind, col)
|
||||
}
|
||||
}
|
||||
},
|
||||
Route::Thread(thread_selection) => Some(ThreadView::scroll_id(
|
||||
thread_selection.selected_or_root(),
|
||||
col,
|
||||
)),
|
||||
Route::Accounts(accounts_route) => match accounts_route {
|
||||
crate::accounts::AccountsRoute::Accounts => Some(AccountsView::scroll_id()),
|
||||
crate::accounts::AccountsRoute::AddAccount => None,
|
||||
},
|
||||
Route::Reply(note_id) => Some(PostReplyView::scroll_id(col, note_id.bytes())),
|
||||
Route::Quote(note_id) => Some(QuoteRepostView::scroll_id(col, note_id.bytes())),
|
||||
Route::Relays => Some(RelayView::scroll_id()),
|
||||
Route::ComposeNote => Some(PostView::scroll_id()),
|
||||
Route::AddColumn(add_column_route) => Some(AddColumnView::scroll_id(add_column_route)),
|
||||
Route::EditProfile(_) => Some(EditProfileView::scroll_id()),
|
||||
Route::Support => None,
|
||||
Route::NewDeck => Some(ConfigureDeckView::scroll_id()),
|
||||
Route::Search => Some(SearchView::scroll_id()),
|
||||
Route::EditDeck(_) => None,
|
||||
Route::Wallet(_) => None,
|
||||
Route::CustomizeZapAmount(_) => None,
|
||||
Route::Settings => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Does the corresponding View for the route use a egui::Frame to wrap the ScrollArea?
|
||||
/// TODO(kernelkind): this is quite hacky...
|
||||
fn route_uses_frame(route: &Route) -> bool {
|
||||
match route {
|
||||
Route::Accounts(accounts_route) => match accounts_route {
|
||||
crate::accounts::AccountsRoute::Accounts => true,
|
||||
crate::accounts::AccountsRoute::AddAccount => false,
|
||||
},
|
||||
Route::Relays => true,
|
||||
Route::Timeline(_) => false,
|
||||
Route::Thread(_) => false,
|
||||
Route::Reply(_) => false,
|
||||
Route::Quote(_) => false,
|
||||
Route::Settings => false,
|
||||
Route::ComposeNote => false,
|
||||
Route::AddColumn(_) => false,
|
||||
Route::EditProfile(_) => false,
|
||||
Route::Support => false,
|
||||
Route::NewDeck => false,
|
||||
Route::Search => false,
|
||||
Route::EditDeck(_) => false,
|
||||
Route::Wallet(_) => false,
|
||||
Route::CustomizeZapAmount(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,8 @@ pub fn render_thread_route(
|
||||
note_options,
|
||||
note_context,
|
||||
jobs,
|
||||
col,
|
||||
)
|
||||
.id_source(col)
|
||||
.ui(ui)
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ impl<'a> AccountsView<'a> {
|
||||
|
||||
ui.add_space(8.0);
|
||||
scroll_area()
|
||||
.id_salt(AccountsView::scroll_id())
|
||||
.show(ui, |ui| {
|
||||
Self::show_accounts(ui, self.accounts, self.ndb, self.img_cache, self.i18n)
|
||||
})
|
||||
@@ -59,6 +60,10 @@ impl<'a> AccountsView<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn scroll_id() -> egui::Id {
|
||||
egui::Id::new("accounts")
|
||||
}
|
||||
|
||||
fn show_accounts(
|
||||
ui: &mut Ui,
|
||||
accounts: &Accounts,
|
||||
|
||||
@@ -64,14 +64,14 @@ enum AddColumnOption {
|
||||
Individual(PubkeySource),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Default, Hash)]
|
||||
pub enum AddAlgoRoute {
|
||||
#[default]
|
||||
Base,
|
||||
LastPerPubkey,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
|
||||
pub enum AddColumnRoute {
|
||||
Base,
|
||||
UndecidedNotification,
|
||||
@@ -187,8 +187,13 @@ impl<'a> AddColumnView<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scroll_id(route: &AddColumnRoute) -> egui::Id {
|
||||
egui::Id::new(("add_column", route))
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, ui: &mut Ui) -> Option<AddColumnResponse> {
|
||||
ScrollArea::vertical()
|
||||
.id_salt(AddColumnView::scroll_id(&AddColumnRoute::Base))
|
||||
.show(ui, |ui| {
|
||||
let mut selected_option: Option<AddColumnResponse> = None;
|
||||
for column_option_data in self.get_base_options(ui) {
|
||||
|
||||
@@ -33,6 +33,10 @@ impl<'a> ConfigureDeckView<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn scroll_id() -> egui::Id {
|
||||
egui::Id::new("configure-deck")
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, ui: &mut Ui) -> Option<ConfigureDeckResponse> {
|
||||
let title_font = egui::FontId::new(
|
||||
notedeck::fonts::get_font_size(ui.ctx(), &NotedeckTextStyle::Heading4),
|
||||
@@ -261,6 +265,7 @@ fn glyph_options_ui(
|
||||
) -> Option<char> {
|
||||
let mut selected_glyph = None;
|
||||
egui::ScrollArea::vertical()
|
||||
.id_salt(ConfigureDeckView::scroll_id())
|
||||
.max_height(max_height)
|
||||
.show(ui, |ui| {
|
||||
let max_width = ui.available_width();
|
||||
|
||||
@@ -35,7 +35,6 @@ pub struct PostView<'a, 'd> {
|
||||
draft: &'a mut Draft,
|
||||
post_type: PostType,
|
||||
poster: FilledKeypair<'a>,
|
||||
id_source: Option<egui::Id>,
|
||||
inner_rect: egui::Rect,
|
||||
note_options: NoteOptions,
|
||||
jobs: &'a mut JobsCache,
|
||||
@@ -112,12 +111,10 @@ impl<'a, 'd> PostView<'a, 'd> {
|
||||
note_options: NoteOptions,
|
||||
jobs: &'a mut JobsCache,
|
||||
) -> Self {
|
||||
let id_source: Option<egui::Id> = None;
|
||||
PostView {
|
||||
note_context,
|
||||
draft,
|
||||
poster,
|
||||
id_source,
|
||||
post_type,
|
||||
inner_rect,
|
||||
note_options,
|
||||
@@ -125,9 +122,12 @@ impl<'a, 'd> PostView<'a, 'd> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id_source(mut self, id_source: impl std::hash::Hash) -> Self {
|
||||
self.id_source = Some(egui::Id::new(id_source));
|
||||
self
|
||||
fn id() -> egui::Id {
|
||||
egui::Id::new("post")
|
||||
}
|
||||
|
||||
pub fn scroll_id() -> egui::Id {
|
||||
PostView::id().with("scroll")
|
||||
}
|
||||
|
||||
fn editbox(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) -> egui::Response {
|
||||
@@ -213,7 +213,8 @@ impl<'a, 'd> PostView<'a, 'd> {
|
||||
|
||||
let focused = out.response.has_focus();
|
||||
|
||||
ui.ctx().data_mut(|d| d.insert_temp(self.id(), focused));
|
||||
ui.ctx()
|
||||
.data_mut(|d| d.insert_temp(PostView::id(), focused));
|
||||
|
||||
out.response
|
||||
}
|
||||
@@ -305,11 +306,7 @@ impl<'a, 'd> PostView<'a, 'd> {
|
||||
|
||||
fn focused(&self, ui: &egui::Ui) -> bool {
|
||||
ui.ctx()
|
||||
.data(|d| d.get_temp::<bool>(self.id()).unwrap_or(false))
|
||||
}
|
||||
|
||||
fn id(&self) -> egui::Id {
|
||||
self.id_source.unwrap_or_else(|| egui::Id::new("post"))
|
||||
.data(|d| d.get_temp::<bool>(PostView::id()).unwrap_or(false))
|
||||
}
|
||||
|
||||
pub fn outer_margin() -> i8 {
|
||||
@@ -321,6 +318,13 @@ impl<'a, 'd> PostView<'a, 'd> {
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, txn: &Transaction, ui: &mut egui::Ui) -> PostResponse {
|
||||
ScrollArea::vertical()
|
||||
.id_salt(PostView::scroll_id())
|
||||
.show(ui, |ui| self.ui_no_scroll(txn, ui))
|
||||
.inner
|
||||
}
|
||||
|
||||
pub fn ui_no_scroll(&mut self, txn: &Transaction, ui: &mut egui::Ui) -> PostResponse {
|
||||
let focused = self.focused(ui);
|
||||
let stroke = if focused {
|
||||
ui.visuals().selection.stroke
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::{
|
||||
ui::{self},
|
||||
};
|
||||
|
||||
use egui::ScrollArea;
|
||||
use enostr::{FilledKeypair, NoteId};
|
||||
use notedeck::NoteContext;
|
||||
use notedeck_ui::{jobs::JobsCache, NoteOptions};
|
||||
@@ -13,7 +14,7 @@ pub struct QuoteRepostView<'a, 'd> {
|
||||
poster: FilledKeypair<'a>,
|
||||
draft: &'a mut Draft,
|
||||
quoting_note: &'a nostrdb::Note<'a>,
|
||||
id_source: Option<egui::Id>,
|
||||
scroll_id: egui::Id,
|
||||
inner_rect: egui::Rect,
|
||||
note_options: NoteOptions,
|
||||
jobs: &'a mut JobsCache,
|
||||
@@ -29,22 +30,36 @@ impl<'a, 'd> QuoteRepostView<'a, 'd> {
|
||||
inner_rect: egui::Rect,
|
||||
note_options: NoteOptions,
|
||||
jobs: &'a mut JobsCache,
|
||||
col: usize,
|
||||
) -> Self {
|
||||
let id_source: Option<egui::Id> = None;
|
||||
QuoteRepostView {
|
||||
note_context,
|
||||
poster,
|
||||
draft,
|
||||
quoting_note,
|
||||
id_source,
|
||||
scroll_id: QuoteRepostView::scroll_id(col, quoting_note.id()),
|
||||
inner_rect,
|
||||
note_options,
|
||||
jobs,
|
||||
}
|
||||
}
|
||||
|
||||
fn id(col: usize, note_id: &[u8; 32]) -> egui::Id {
|
||||
egui::Id::new(("quote_repost", col, note_id))
|
||||
}
|
||||
|
||||
pub fn scroll_id(col: usize, note_id: &[u8; 32]) -> egui::Id {
|
||||
QuoteRepostView::id(col, note_id).with("scroll")
|
||||
}
|
||||
|
||||
pub fn show(&mut self, ui: &mut egui::Ui) -> PostResponse {
|
||||
let id = self.id();
|
||||
ScrollArea::vertical()
|
||||
.id_salt(self.scroll_id)
|
||||
.show(ui, |ui| self.show_internal(ui))
|
||||
.inner
|
||||
}
|
||||
|
||||
fn show_internal(&mut self, ui: &mut egui::Ui) -> PostResponse {
|
||||
let quoting_note_id = self.quoting_note.id();
|
||||
|
||||
let post_resp = ui::PostView::new(
|
||||
@@ -56,18 +71,7 @@ impl<'a, 'd> QuoteRepostView<'a, 'd> {
|
||||
self.note_options,
|
||||
self.jobs,
|
||||
)
|
||||
.id_source(id)
|
||||
.ui(self.quoting_note.txn().unwrap(), ui);
|
||||
.ui_no_scroll(self.quoting_note.txn().unwrap(), ui);
|
||||
post_resp
|
||||
}
|
||||
|
||||
pub fn id_source(mut self, id: egui::Id) -> Self {
|
||||
self.id_source = Some(id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn id(&self) -> egui::Id {
|
||||
self.id_source
|
||||
.unwrap_or_else(|| egui::Id::new("quote-repost-view"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::ui::{
|
||||
note::{PostAction, PostResponse, PostType},
|
||||
};
|
||||
|
||||
use egui::{Rect, Response, Ui};
|
||||
use egui::{Rect, Response, ScrollArea, Ui};
|
||||
use enostr::{FilledKeypair, NoteId};
|
||||
use notedeck::NoteContext;
|
||||
use notedeck_ui::jobs::JobsCache;
|
||||
@@ -15,7 +15,7 @@ pub struct PostReplyView<'a, 'd> {
|
||||
poster: FilledKeypair<'a>,
|
||||
draft: &'a mut Draft,
|
||||
note: &'a nostrdb::Note<'a>,
|
||||
id_source: Option<egui::Id>,
|
||||
scroll_id: egui::Id,
|
||||
inner_rect: egui::Rect,
|
||||
note_options: NoteOptions,
|
||||
jobs: &'a mut JobsCache,
|
||||
@@ -31,31 +31,37 @@ impl<'a, 'd> PostReplyView<'a, 'd> {
|
||||
inner_rect: egui::Rect,
|
||||
note_options: NoteOptions,
|
||||
jobs: &'a mut JobsCache,
|
||||
col: usize,
|
||||
) -> Self {
|
||||
let id_source: Option<egui::Id> = None;
|
||||
PostReplyView {
|
||||
note_context,
|
||||
poster,
|
||||
draft,
|
||||
note,
|
||||
id_source,
|
||||
scroll_id: PostReplyView::scroll_id(col, note.id()),
|
||||
inner_rect,
|
||||
note_options,
|
||||
jobs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id_source(mut self, id: egui::Id) -> Self {
|
||||
self.id_source = Some(id);
|
||||
self
|
||||
fn id(col: usize, note_id: &[u8; 32]) -> egui::Id {
|
||||
egui::Id::new(("reply_view", col, note_id))
|
||||
}
|
||||
|
||||
pub fn id(&self) -> egui::Id {
|
||||
self.id_source
|
||||
.unwrap_or_else(|| egui::Id::new("post-reply-view"))
|
||||
pub fn scroll_id(col: usize, note_id: &[u8; 32]) -> egui::Id {
|
||||
PostReplyView::id(col, note_id).with("scroll")
|
||||
}
|
||||
|
||||
pub fn show(&mut self, ui: &mut egui::Ui) -> PostResponse {
|
||||
ScrollArea::vertical()
|
||||
.id_salt(self.scroll_id)
|
||||
.show(ui, |ui| self.show_internal(ui))
|
||||
.inner
|
||||
}
|
||||
|
||||
// no scroll
|
||||
fn show_internal(&mut self, ui: &mut egui::Ui) -> PostResponse {
|
||||
ui.vertical(|ui| {
|
||||
let avail_rect = ui.available_rect_before_wrap();
|
||||
|
||||
@@ -81,7 +87,6 @@ impl<'a, 'd> PostReplyView<'a, 'd> {
|
||||
})
|
||||
.inner;
|
||||
|
||||
let id = self.id();
|
||||
let replying_to = self.note.id();
|
||||
let rect_before_post = ui.min_rect();
|
||||
|
||||
@@ -95,8 +100,7 @@ impl<'a, 'd> PostReplyView<'a, 'd> {
|
||||
self.note_options,
|
||||
self.jobs,
|
||||
)
|
||||
.id_source(id)
|
||||
.ui(self.note.txn().unwrap(), ui)
|
||||
.ui_no_scroll(self.note.txn().unwrap(), ui)
|
||||
};
|
||||
|
||||
post_response.action = post_response
|
||||
|
||||
@@ -24,9 +24,14 @@ impl<'a> EditProfileView<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scroll_id() -> egui::Id {
|
||||
egui::Id::new("edit_profile")
|
||||
}
|
||||
|
||||
// return true to save
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> bool {
|
||||
ScrollArea::vertical()
|
||||
.id_salt(EditProfileView::scroll_id())
|
||||
.show(ui, |ui| {
|
||||
banner(ui, self.state.banner(), 188.0);
|
||||
|
||||
|
||||
@@ -59,8 +59,12 @@ impl<'a, 'd> ProfileView<'a, 'd> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scroll_id(col_id: usize, profile_pubkey: &Pubkey) -> egui::Id {
|
||||
egui::Id::new(("profile_scroll", col_id, profile_pubkey))
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<ProfileViewAction> {
|
||||
let scroll_id = egui::Id::new(("profile_scroll", self.col_id, self.pubkey));
|
||||
let scroll_id = ProfileView::scroll_id(self.col_id, self.pubkey);
|
||||
let offset_id = scroll_id.with("scroll_offset");
|
||||
|
||||
let mut scroll_area = ScrollArea::vertical().id_salt(scroll_id);
|
||||
|
||||
@@ -36,6 +36,7 @@ impl RelayView<'_> {
|
||||
ui.add_space(8.0);
|
||||
|
||||
egui::ScrollArea::vertical()
|
||||
.id_salt(RelayView::scroll_id())
|
||||
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden)
|
||||
.auto_shrink([false; 2])
|
||||
.show(ui, |ui| {
|
||||
@@ -51,6 +52,10 @@ impl RelayView<'_> {
|
||||
|
||||
action
|
||||
}
|
||||
|
||||
pub fn scroll_id() -> egui::Id {
|
||||
egui::Id::new("relay_scroll")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RelayView<'a> {
|
||||
|
||||
@@ -151,6 +151,7 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
||||
|
||||
fn show_search_results(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
|
||||
egui::ScrollArea::vertical()
|
||||
.id_salt(SearchView::scroll_id())
|
||||
.show(ui, |ui| {
|
||||
let reversed = false;
|
||||
TimelineTabView::new(
|
||||
@@ -165,6 +166,10 @@ impl<'a, 'd> SearchView<'a, 'd> {
|
||||
})
|
||||
.inner
|
||||
}
|
||||
|
||||
pub fn scroll_id() -> egui::Id {
|
||||
egui::Id::new("search_results")
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_search(
|
||||
|
||||
@@ -14,7 +14,6 @@ pub struct ThreadView<'a, 'd> {
|
||||
selected_note_id: &'a [u8; 32],
|
||||
note_options: NoteOptions,
|
||||
col: usize,
|
||||
id_source: egui::Id,
|
||||
note_context: &'a mut NoteContext<'d>,
|
||||
jobs: &'a mut JobsCache,
|
||||
}
|
||||
@@ -27,37 +26,33 @@ impl<'a, 'd> ThreadView<'a, 'd> {
|
||||
note_options: NoteOptions,
|
||||
note_context: &'a mut NoteContext<'d>,
|
||||
jobs: &'a mut JobsCache,
|
||||
col: usize,
|
||||
) -> Self {
|
||||
let id_source = egui::Id::new("threadscroll_threadview");
|
||||
ThreadView {
|
||||
threads,
|
||||
selected_note_id,
|
||||
note_options,
|
||||
id_source,
|
||||
note_context,
|
||||
jobs,
|
||||
col: 0,
|
||||
col,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id_source(mut self, col: usize) -> Self {
|
||||
self.col = col;
|
||||
self.id_source = egui::Id::new(("threadscroll", col));
|
||||
self
|
||||
pub fn scroll_id(selected_note_id: &[u8; 32], col: usize) -> egui::Id {
|
||||
egui::Id::new(("threadscroll", selected_note_id, col))
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
|
||||
let txn = Transaction::new(self.note_context.ndb).expect("txn");
|
||||
|
||||
let scroll_id = ThreadView::scroll_id(self.selected_note_id, self.col);
|
||||
let mut scroll_area = egui::ScrollArea::vertical()
|
||||
.id_salt(self.id_source)
|
||||
.id_salt(scroll_id)
|
||||
.animated(false)
|
||||
.auto_shrink([false, false])
|
||||
.scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysVisible);
|
||||
|
||||
let offset_id = self
|
||||
.id_source
|
||||
.with(("scroll_offset", self.selected_note_id));
|
||||
let offset_id = scroll_id.with(("scroll_offset", self.selected_note_id));
|
||||
|
||||
if let Some(offset) = ui.data(|i| i.get_temp::<f32>(offset_id)) {
|
||||
scroll_area = scroll_area.vertical_scroll_offset(offset);
|
||||
|
||||
@@ -74,6 +74,15 @@ impl<'a, 'd> TimelineView<'a, 'd> {
|
||||
self.reverse = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn scroll_id(
|
||||
timeline_cache: &TimelineCache,
|
||||
timeline_id: &TimelineKind,
|
||||
col: usize,
|
||||
) -> Option<egui::Id> {
|
||||
let timeline = timeline_cache.get(timeline_id)?;
|
||||
Some(egui::Id::new(("tlscroll", timeline.view_id(col))))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -95,7 +104,9 @@ fn timeline_ui(
|
||||
|
||||
*/
|
||||
|
||||
let scroll_id = {
|
||||
let scroll_id = TimelineView::scroll_id(timeline_cache, timeline_id, col)?;
|
||||
|
||||
{
|
||||
let timeline = if let Some(timeline) = timeline_cache.get_mut(timeline_id) {
|
||||
timeline
|
||||
} else {
|
||||
@@ -114,8 +125,6 @@ fn timeline_ui(
|
||||
|
||||
// need this for some reason??
|
||||
ui.add_space(3.0);
|
||||
|
||||
egui::Id::new(("tlscroll", timeline.view_id(col)))
|
||||
};
|
||||
|
||||
let show_top_button_id = ui.id().with((scroll_id, "at_top"));
|
||||
|
||||
Reference in New Issue
Block a user