looks good. I added some fixup commits:
William Casarin (2):
popup: increase fixed window margin
refactor: use map instead of explicit `if let`
We will likely need a new window control to match our figma design. This
shouldn't be too hard to do since it's basically a simple frame + label.
On Sun, Jun 23, 2024 at 07:46:58PM GMT, kernelkind wrote:
>Closes:
https://github.com/damus-io/notedeck/pull/108
>---
> src/
app.rs | 14 +++++---
> src/
fixed_window.rs | 62 ++++++++++++++++++++++++++++++++++++
> src/
lib.rs | 1 +
> src/
route.rs | 24 +++++++++++++-
> src/ui/
account_management.rs | 30 +++++++++++------
> src/ui/
account_switcher.rs | 9 ++++--
> src/ui/
global_popup.rs | 53 ++++++++++++++++++++++++++++++
> src/ui/
mod.rs | 2 ++
> src/ui/
side_panel.rs | 7 ++--
> 9 files changed, 181 insertions(+), 21 deletions(-)
> create mode 100644 src/
fixed_window.rs
> create mode 100644 src/ui/
global_popup.rs
>
>diff --git a/src/
app.rs b/src/
app.rs
>index a6dbe3e..3c00a27 100644
>--- a/src/
app.rs
>+++ b/src/
app.rs
>@@ -10,7 +10,7 @@ use crate::relay_pool_manager::RelayPoolManager;
> use crate::route::Route;
> use crate::timeline;
> use crate::timeline::{MergeKind, NoteRef, Timeline, ViewFilter};
>-use crate::ui::{self, AccountSelectionWidget};
>+use crate::ui::{self, AccountSelectionWidget, DesktopGlobalPopup};
> use crate::ui::{DesktopSidePanel, RelayView, View};
> use crate::Result;
> use egui_nav::{Nav, NavAction};
>@@ -46,7 +46,7 @@ pub struct Damus {
> is_mobile: bool,
>
> /// global navigation for account management popups, etc.
>- //nav: Vec<Route>,
>+ pub global_nav: Vec<Route>,
> pub textmode: bool,
> pub drafts: HashMap<enostr::NoteId, Draft>,
>
>@@ -59,6 +59,7 @@ pub struct Damus {
>
> frame_history: crate::frame_history::FrameHistory,
> pub show_account_switcher: bool,
>+ pub show_global_popup: bool,
> }
>
> fn relay_setup(pool: &mut RelayPool, ctx: &egui::Context) {
>@@ -728,6 +729,8 @@ impl Damus {
> //compose: "".to_string(),
> frame_history: FrameHistory::default(),
> show_account_switcher: false,
>+ show_global_popup: false,
>+ global_nav: Vec::new(),
> }
> }
>
>@@ -755,6 +758,8 @@ impl Damus {
> account_manager: AccountManager::new(None, crate::key_storage::KeyStorage::None),
> frame_history: FrameHistory::default(),
> show_account_switcher: false,
>+ show_global_popup: true,
>+ global_nav: Vec::new(),
> }
> }
>
>@@ -990,9 +995,8 @@ fn render_damus_desktop(ctx: &egui::Context, app: &mut Damus) {
>
> main_panel(&ctx.style(), app.is_mobile()).show(ctx, |ui| {
> ui.spacing_mut().item_spacing.x = 0.0;
>- if app.show_account_switcher {
>- AccountSelectionWidget::ui(app, ui);
>- }
>+ AccountSelectionWidget::ui(app, ui);
>+ DesktopGlobalPopup::show(app.global_nav.clone(), app, ui);
> if need_scroll {
> egui::ScrollArea::horizontal().show(ui, |ui| {
> timelines_view(ui, panel_sizes, app, app.timelines.len());
>diff --git a/src/
fixed_window.rs b/src/
fixed_window.rs
>new file mode 100644
>index 0000000..e416074
>--- /dev/null
>+++ b/src/
fixed_window.rs
>@@ -0,0 +1,62 @@
>+use egui::{Rect, Response, RichText, Sense, Window};
>+
>+pub struct FixedWindow {
>+ title: Option<RichText>,
>+}
>+
>+#[derive(PartialEq)]
>+pub enum FixedWindowResponse {
>+ Opened,
>+ Closed,
>+}
>+
>+impl FixedWindow {
>+ #[allow(dead_code)]
>+ pub fn new() -> Self {
>+ Self { title: None }
>+ }
>+
>+ pub fn maybe_with_title(maybe_title: Option<RichText>) -> Self {
>+ Self { title: maybe_title }
>+ }
>+
>+ #[allow(dead_code)]
>+ pub fn with_title(mut self, title: RichText) -> Self {
>+ self.title = Some(title);
>+ self
>+ }
>+
>+ pub fn show(
>+ self,
>+ ui: &mut egui::Ui,
>+ rect: Rect,
>+ add_contents: impl FnOnce(&mut egui::Ui) -> Response,
>+ ) -> FixedWindowResponse {
>+ let mut is_open = true;
>+
>+ let use_title_bar = self.title.is_some();
>+ let title = if let Some(title) = self.title {
>+ title
>+ } else {
>+ RichText::new("")
>+ };
>+
>+ Window::new(title)
>+ .open(&mut is_open)
>+ .fixed_rect(rect)
>+ .collapsible(false)
>+ .movable(false)
>+ .resizable(false)
>+ .title_bar(use_title_bar)
>+ .show(ui.ctx(), |ui| {
>+ let resp = add_contents(ui);
>+ ui.allocate_rect(resp.rect, Sense::hover())
>+ });
>+
>+ if !is_open {
>+ FixedWindowResponse::Closed
>+ } else {
>+ FixedWindowResponse::Opened
>+ }
>+ }
>+}
>diff --git a/src/
lib.rs b/src/
lib.rs
>index ef7425b..bb1cde9 100644
>--- a/src/
lib.rs
>+++ b/src/
lib.rs
>@@ -10,6 +10,7 @@ mod app_style;
> mod colors;
> mod draft;
> mod filter;
>+mod fixed_window;
> mod fonts;
> mod frame_history;
> mod images;
>diff --git a/src/
route.rs b/src/
route.rs
>index 706654f..5518452 100644
>--- a/src/
route.rs
>+++ b/src/
route.rs
>@@ -1,5 +1,8 @@
>+use egui::{Response, RichText};
> use enostr::NoteId;
>-use std::fmt;
>+use std::fmt::{self};
>+
>+use crate::{ui::AccountManagementView, Damus};
>
> /// App routing. These describe different places you can go inside Notedeck.
> #[derive(Clone, Debug)]
>@@ -22,3 +25,22 @@ impl fmt::Display for Route {
> }
> }
> }
>+
>+impl Route {
>+ pub fn show_global_popup(&self, app: &mut Damus, ui: &mut egui::Ui) -> Option<Response> {
>+ match self {
>+ Route::ManageAccount => AccountManagementView::ui(app, ui),
>+ _ => None,
>+ }
>+ }
>+
>+ pub fn title(&self) -> RichText {
>+ match self {
>+ Route::ManageAccount => RichText::new("Manage Account").size(24.0),
>+ Route::Thread(_) => RichText::new("Thread"),
>+ Route::Reply(_) => RichText::new("Reply"),
>+ Route::Relays => RichText::new("Relays"),
>+ Route::Timeline(_) => RichText::new("Timeline"),
>+ }
>+ }
>+}
>diff --git a/src/ui/
account_management.rs b/src/ui/
account_management.rs
>index d8b377b..7253024 100644
>--- a/src/ui/
account_management.rs
>+++ b/src/ui/
account_management.rs
>@@ -5,7 +5,7 @@ use crate::{
> ui::{profile_preview_controller, Preview, PreviewConfig, View},
> Damus,
> };
>-use egui::{Align, Button, Frame, Image, Layout, RichText, ScrollArea, Vec2};
>+use egui::{Align, Button, Frame, Image, Layout, Response, RichText, ScrollArea, Vec2};
>
> use super::profile::preview::SimpleProfilePreview;
> use super::profile::ProfilePreviewOp;
>@@ -13,14 +13,26 @@ use super::profile::ProfilePreviewOp;
> pub struct AccountManagementView {}
>
> impl AccountManagementView {
>- fn show(app: &mut Damus, ui: &mut egui::Ui) {
>- Frame::none().outer_margin(24.0).show(ui, |ui| {
>- Self::top_section_buttons_widget(ui);
>- ui.add_space(8.0);
>- scroll_area().show(ui, |ui| {
>- Self::show_accounts(app, ui);
>- });
>- });
>+ pub fn ui(app: &mut Damus, ui: &mut egui::Ui) -> Option<Response> {
>+ if app.is_mobile() {
>+ AccountManagementView::show_mobile(app, ui);
>+ None
>+ } else {
>+ Some(AccountManagementView::show(app, ui))
>+ }
>+ }
>+
>+ fn show(app: &mut Damus, ui: &mut egui::Ui) -> Response {
>+ Frame::none()
>+ .outer_margin(24.0)
>+ .show(ui, |ui| {
>+ Self::top_section_buttons_widget(ui);
>+ ui.add_space(8.0);
>+ scroll_area().show(ui, |ui| {
>+ Self::show_accounts(app, ui);
>+ });
>+ })
>+ .response
> }
>
> fn show_accounts(app: &mut Damus, ui: &mut egui::Ui) {
>diff --git a/src/ui/
account_switcher.rs b/src/ui/
account_switcher.rs
>index 9fafd1c..5f52296 100644
>--- a/src/ui/
account_switcher.rs
>+++ b/src/ui/
account_switcher.rs
>@@ -1,5 +1,5 @@
> use crate::{
>- account_manager::UserAccount, colors::PINK, profile::DisplayName,
>+ account_manager::UserAccount, colors::PINK, profile::DisplayName, route::Route,
> ui::profile_preview_controller, Damus, Result,
> };
>
>@@ -27,6 +27,10 @@ struct AccountSelectResponse {
>
> impl AccountSelectionWidget {
> pub fn ui(app: &mut Damus, ui: &mut egui::Ui) {
>+ if !app.show_account_switcher {
>+ return;
>+ }
>+
> if app.is_mobile() {
> Self::show_mobile(ui);
> } else {
>@@ -54,7 +58,8 @@ impl AccountSelectionWidget {
> }
> AccountSelectAction::OpenAccountManagement => {
> app.show_account_switcher = false;
>- // TODO: push account management to global popup router
>+ app.global_nav.push(Route::ManageAccount);
>+ app.show_global_popup = true;
> }
> }
> }
>diff --git a/src/ui/
global_popup.rs b/src/ui/
global_popup.rs
>new file mode 100644
>index 0000000..a0b949c
>--- /dev/null
>+++ b/src/ui/
global_popup.rs
>@@ -0,0 +1,53 @@
>+use std::{cell::RefCell, rc::Rc};
>+
>+use egui::Sense;
>+use egui_nav::{Nav, NavAction};
>+
>+use crate::{
>+ fixed_window::{FixedWindow, FixedWindowResponse},
>+ route::Route,
>+ Damus,
>+};
>+
>+static MARGIN: f32 = 100.0;
>+
>+pub struct DesktopGlobalPopup {}
>+
>+impl DesktopGlobalPopup {
>+ pub fn show(routes: Vec<Route>, app: &mut Damus, ui: &mut egui::Ui) {
>+ if routes.is_empty() || !app.show_global_popup {
>+ return;
>+ }
>+
>+ let rect = ui.ctx().screen_rect().shrink(MARGIN);
>+ let title = if let Some(first) = routes.first() {
>+ // TODO(kernelkind): not a great way of getting the title of the routes 'grouping'
>+ Some(first.title())
>+ } else {
>+ None
>+ };
>+
>+ let app_ctx = Rc::new(RefCell::new(app));
>+
>+ let resp = FixedWindow::maybe_with_title(title).show(ui, rect, |ui| {
>+ let nav_response = Nav::new(routes).navigating(false).show(ui, |ui, nav| {
>+ if let Some(resp) = nav.top().show_global_popup(&mut app_ctx.borrow_mut(), ui) {
>+ ui.allocate_rect(resp.rect, Sense::hover())
>+ } else {
>+ ui.label("") // TODO(kernelkind): not a great practice
>+ }
>+ });
>+
>+ if let Some(NavAction::Returned) = nav_response.action {
>+ app_ctx.borrow_mut().global_nav.pop();
>+ }
>+
>+ nav_response.inner
>+ });
>+
>+ if resp == FixedWindowResponse::Closed {
>+ app_ctx.borrow_mut().global_nav.pop();
>+ app_ctx.borrow_mut().show_global_popup = false;
>+ }
>+ }
>+}
>diff --git a/src/ui/
mod.rs b/src/ui/
mod.rs
>index 00aee4a..7f5bdd5 100644
>--- a/src/ui/
mod.rs
>+++ b/src/ui/
mod.rs
>@@ -2,6 +2,7 @@ pub mod account_login_view;
> pub mod account_management;
> pub mod account_switcher;
> pub mod anim;
>+pub mod global_popup;
> pub mod mention;
> pub mod note;
> pub mod preview;
>@@ -12,6 +13,7 @@ pub mod username;
>
> pub use account_management::AccountManagementView;
> pub use account_switcher::AccountSelectionWidget;
>+pub use global_popup::DesktopGlobalPopup;
> pub use mention::Mention;
> pub use note::{BarAction, Note, NoteResponse, PostView};
> pub use preview::{Preview, PreviewApp, PreviewConfig};
>diff --git a/src/ui/
side_panel.rs b/src/ui/
side_panel.rs
>index 0c959d8..1c4e7c2 100644
>--- a/src/ui/
side_panel.rs
>+++ b/src/ui/
side_panel.rs
>@@ -127,7 +127,7 @@ mod preview {
>
> use crate::{
> test_data,
>- ui::{AccountSelectionWidget, Preview, PreviewConfig},
>+ ui::{AccountSelectionWidget, DesktopGlobalPopup, Preview, PreviewConfig},
> };
>
> use super::*;
>@@ -157,9 +157,8 @@ mod preview {
> });
> });
>
>- if self.app.show_account_switcher {
>- AccountSelectionWidget::ui(&mut
self.app, ui);
>- }
>+ AccountSelectionWidget::ui(&mut
self.app, ui);
>+ DesktopGlobalPopup::show(self.app.global_nav.clone(), &mut
self.app, ui);
> }
> }
>