diff --git a/gossip-bin/src/ui/relays/active.rs b/gossip-bin/src/ui/relays/active.rs index 8260eb30d..f8c88addc 100644 --- a/gossip-bin/src/ui/relays/active.rs +++ b/gossip-bin/src/ui/relays/active.rs @@ -23,7 +23,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr super::relay_sort_combo(app, ui); btn_h_space!(ui); widgets::TextEdit::search(&app.theme, &app.assets, &mut app.relays.search) - .desired_width(150.0) + .desired_width(super::SEARCH_WIDTH) .show(ui); if widgets::Button::primary(&app.theme, "Add Relay") .show(ui) diff --git a/gossip-bin/src/ui/relays/known.rs b/gossip-bin/src/ui/relays/known.rs index 8da2c4c4f..e0376f021 100644 --- a/gossip-bin/src/ui/relays/known.rs +++ b/gossip-bin/src/ui/relays/known.rs @@ -19,7 +19,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr super::relay_sort_combo(app, ui); btn_h_space!(ui); widgets::TextEdit::search(&app.theme, &app.assets, &mut app.relays.search) - .desired_width(150.0) + .desired_width(super::SEARCH_WIDTH) .show(ui); if widgets::Button::primary(&app.theme, "Add Relay") .show(ui) diff --git a/gossip-bin/src/ui/relays/mine.rs b/gossip-bin/src/ui/relays/mine.rs index 8a640f2b8..45dd6e384 100644 --- a/gossip-bin/src/ui/relays/mine.rs +++ b/gossip-bin/src/ui/relays/mine.rs @@ -3,10 +3,8 @@ use crate::ui::{widgets, Page}; use eframe::egui; use egui::{Context, Ui}; use egui_winit::egui::Id; -use gossip_lib::comms::ToOverlordMessage; use gossip_lib::Relay; use gossip_lib::GLOBALS; -use std::sync::atomic::Ordering; pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) { let is_editing = app.relays.edit.is_some(); @@ -21,7 +19,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr super::relay_sort_combo(app, ui); btn_h_space!(ui); widgets::TextEdit::search(&app.theme, &app.assets, &mut app.relays.search) - .desired_width(150.0) + .desired_width(super::SEARCH_WIDTH) .show(ui); if widgets::Button::primary(&app.theme, "Add Relay") .show(ui) @@ -30,26 +28,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr super::start_entry_dialog(app); } - let advertise_remaining = GLOBALS.advertise_jobs_remaining.load(Ordering::Relaxed); - if advertise_remaining == 0 { - if widgets::Button::secondary(&app.theme,"Advertise Relay List") - .show(ui) - .on_hover_text("Advertise my relays. Will send your relay usage information to every relay that seems to be working well so that other people know how to follow and contact you.") - .clicked() - { - let _ = GLOBALS - .to_overlord - .send(ToOverlordMessage::AdvertiseRelayList); - } - } else { - ui.add_enabled( - false, - widgets::Button::secondary( - &app.theme, - format!("Advertising, {} to go", advertise_remaining), - ), - ); - } + // let advertise_remaining = GLOBALS.advertise_jobs_remaining.load(Ordering::Relaxed); }); let relays = if !is_editing { diff --git a/gossip-bin/src/ui/relays/mod.rs b/gossip-bin/src/ui/relays/mod.rs index e24978edc..9a2710fa8 100644 --- a/gossip-bin/src/ui/relays/mod.rs +++ b/gossip-bin/src/ui/relays/mod.rs @@ -15,6 +15,7 @@ mod coverage; mod known; mod mine; +pub const SEARCH_WIDTH: f32 = 80.0; pub const RELAY_URL_PREPOPULATE: &str = "wss://"; pub(super) struct RelayUi { @@ -244,7 +245,9 @@ pub(super) fn relay_scroll_list( if let Some(ref assignment) = GLOBALS.relay_picker.get_relay_assignment(&db_url) { widget.set_user_count(assignment.pubkeys.len()); } - let response = ui.add_enabled(enabled, widget.clone()); + let response = ui + .add_enabled_ui(enabled, |ui| widget.show(ui, &app.theme)) + .inner; if response.clicked() { if !edit { app.relays.edit = Some(db_url); @@ -432,7 +435,7 @@ pub(super) fn configure_list_btn(app: &mut GossipUi, ui: &mut Ui) { let max_size = vec2(180.0, ui.ctx().available_rect().height()); let text = egui::RichText::new("=").size(13.0); - let response = widgets::Button::primary(&app.theme, text) + let response = widgets::Button::secondary(&app.theme, text) .small(true) .show(ui); let menu = widgets::MoreMenu::bubble(ui.next_auto_id(), min_size, max_size); @@ -462,6 +465,20 @@ pub(super) fn configure_list_btn(app: &mut GossipUi, ui: &mut Ui) { }), ))); + if app.page == Page::RelaysMine { + items.push(MoreMenuItem::Button(MoreMenuButton::new("Advertise Relay List", + Box::new(|_ui, _app| { + let _ = GLOBALS + .to_overlord + .send(ToOverlordMessage::AdvertiseRelayList); + + })) + .enabled(GLOBALS.identity.is_unlocked()) + .on_disabled_hover_text("Add or unlock your private key to advertise your relays") + .on_hover_text("Advertise my relays. Will send your relay usage information to every relay that seems to be working well so that other people know how to follow and contact you.") + )); + } + menu.show_entries(ui, app, response, items); }); } @@ -604,7 +621,7 @@ pub(super) fn sort_relay(rui: &RelayUi, a: &Relay, b: &Relay) -> Ordering { .then(b.get_usage_bits_for_sorting().cmp(&a.get_usage_bits_for_sorting())) .then(b.is_good_for_advertise().cmp(&a.is_good_for_advertise())) .then(a.url.cmp(&b.url)), - RelaySorting::Name => a.url.cmp(&b.url), + RelaySorting::Name => a.url.host().cmp(&b.url.host()).then(a.url.cmp(&b.url)), RelaySorting::WriteRelays => b.has_usage_bits(Relay::WRITE) .cmp(&a.has_usage_bits(Relay::WRITE)) .then(a.url.cmp(&b.url)), diff --git a/gossip-bin/src/ui/widgets/mod.rs b/gossip-bin/src/ui/widgets/mod.rs index f275e2873..2d4764325 100644 --- a/gossip-bin/src/ui/widgets/mod.rs +++ b/gossip-bin/src/ui/widgets/mod.rs @@ -14,7 +14,7 @@ pub(crate) mod list_entry; pub use copy_button::{CopyButton, COPY_SYMBOL_SIZE}; mod nav_item; -use eframe::egui::{vec2, FontId, Galley, Rect}; +use eframe::egui::{vec2, FontId, Galley, Pos2, Rect}; use egui_winit::egui::text::LayoutJob; use egui_winit::egui::{ self, Align, FontSelection, Response, RichText, Rounding, Sense, Ui, WidgetText, @@ -137,8 +137,98 @@ pub fn relay_url(ui: &mut Ui, theme: &Theme, url: &RelayUrl) -> Response { font.size *= 0.7; ui.painter().text( - response.rect.left_top(), - egui::Align2::CENTER_TOP, + response.rect.left_center(), + egui::Align2::CENTER_CENTER, + symbol, + font, + color, + ); + + response +} + +pub fn relay_url_at( + ui: &mut Ui, + theme: &Theme, + pos: Pos2, + max_width: f32, + url: &RelayUrl, + font_size: Option, + with_path: bool, +) -> Response { + let (symbol, color, spacer) = if url.as_url_crate_url().scheme() != "wss" { + ( + "\u{00A0}\u{00A0}\u{1F513}", + theme.red_500(), + "\u{00A0}\u{00A0}\u{00A0}", + ) + } else { + ("", theme.accent_color(), "") + }; + let url = url.as_url_crate_url(); + let text = format!("{}{}", spacer, url.host_str().unwrap_or_default()); + let text = if let Some(size) = font_size { + RichText::new(text).size(size) + } else { + RichText::new(text) + }; + + let rect = if !with_path { + let galley = list_entry::text_to_galley_max_width(ui, text.into(), Align::LEFT, max_width); + let rect = + list_entry::draw_text_galley_at(ui, pos, galley, Some(theme.accent_color()), None); + rect + } else { + let galley = list_entry::text_to_galley_max_width(ui, text.into(), Align::LEFT, max_width); + let max_width = max_width - galley.rect.width(); + + let path_text = if let Some(host) = url.host_str() { + url.as_str() + .split_once(host) + .map_or( + None, + |(_before, after)| if after.len() > 1 { Some(after) } else { None }, + ) + } else { + url.as_str() + .split_once("/") + .map_or(None, |(_before, after)| Some(after)) + }; + + let path_text = format!( + "\u{00A0}\u{00A0}{}", + path_text.unwrap_or_default().trim_end_matches('/') + ); + + let path_text = if let Some(size) = font_size { + RichText::new(path_text).size(size) + } else { + RichText::new(path_text) + }; + let path_galley = + list_entry::text_to_galley_max_width(ui, path_text.into(), Align::LEFT, max_width); + + let rect = + list_entry::draw_text_galley_at(ui, pos, galley, Some(theme.accent_color()), None); + let path_rect = list_entry::draw_text_galley_at( + ui, + pos + vec2(rect.width(), 0.0), + path_galley, + None, + None, + ); + + rect.union(path_rect) + }; + + let response = ui.interact(rect, ui.next_auto_id().with("rtitle"), Sense::hover()); + + let mut font = FontId::default(); + font.size = font_size.unwrap_or(font.size) * 0.7; + + ui.painter().text( + response.rect.left_center(), + egui::Align2::CENTER_CENTER, symbol, font, color, diff --git a/gossip-bin/src/ui/widgets/more_menu.rs b/gossip-bin/src/ui/widgets/more_menu.rs index ece65c20a..a5566cb4a 100644 --- a/gossip-bin/src/ui/widgets/more_menu.rs +++ b/gossip-bin/src/ui/widgets/more_menu.rs @@ -33,6 +33,8 @@ pub(in crate::ui) enum MoreMenuItem<'a> { #[allow(clippy::type_complexity)] pub(in crate::ui) struct MoreMenuButton<'a> { text: WidgetText, + on_hover_text: Option, + on_disabled_hover_text: Option, action: Box, enabled: bool, } @@ -45,11 +47,26 @@ impl<'a> MoreMenuButton<'a> { ) -> Self { Self { text: text.into(), + on_hover_text: None, + on_disabled_hover_text: None, action, enabled: true, } } + /// Set an optional `on_hover_text` + pub fn on_hover_text(mut self, text: impl Into) -> Self { + self.on_hover_text = Some(text.into()); + self + } + + /// Set an optional `on_disabled_hover_text` + pub fn on_disabled_hover_text(mut self, text: impl Into) -> Self { + self.on_disabled_hover_text = Some(text.into()); + self + } + + /// Set `enabled` state of this button pub fn enabled(mut self, enabled: bool) -> Self { self.enabled = enabled; self @@ -71,7 +88,14 @@ impl<'a> MoreMenuButton<'a> { ui.disable(); } - let response = draw_menu_button(ui, &app.theme, self.text, None); + let response = draw_menu_button( + ui, + &app.theme, + self.text, + None, + self.on_hover_text, + self.on_disabled_hover_text, + ); // process action if response.clicked() { @@ -141,7 +165,7 @@ impl<'a> MoreMenuSubMenu<'a> { let mut open = load_state(ui, &self.id); - let response = draw_menu_button(ui, &app.theme, self.title, Some(open)); + let response = draw_menu_button(ui, &app.theme, self.title, Some(open), None, None); // TODO paint open/close arrow, use animation @@ -779,12 +803,14 @@ fn draw_menu_button( theme: &Theme, title: WidgetText, force_hover: Option, + on_hover_text: Option, + on_disabled_hover_text: Option, ) -> Response { // layout let desired_size = vec2(ui.available_width(), 32.0); // interact - let (rect, response) = ui.allocate_at_least(desired_size, Sense::click()); + let (rect, mut response) = ui.allocate_at_least(desired_size, Sense::click()); response.widget_info(|| WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), title.text())); let state = super::interact_widget_state(ui, &response); let state = match state { @@ -902,5 +928,12 @@ fn draw_menu_button( ); painter.add(shapes); + if let Some(text) = on_hover_text { + response = response.on_hover_text(text); + } + if let Some(text) = on_disabled_hover_text { + response = response.on_disabled_hover_text(text); + } + response } diff --git a/gossip-bin/src/ui/widgets/relay_entry.rs b/gossip-bin/src/ui/widgets/relay_entry.rs index 7e1f8e434..1ea454c9d 100644 --- a/gossip-bin/src/ui/widgets/relay_entry.rs +++ b/gossip-bin/src/ui/widgets/relay_entry.rs @@ -3,7 +3,7 @@ use eframe::egui::{self, *}; use nostr_types::{PublicKeyHex, RelayUrl, Unixtime}; use std::fmt; -use crate::ui::{widgets, GossipUi}; +use crate::ui::{widgets, GossipUi, Theme}; use gossip_lib::{comms::ToOverlordMessage, Relay, GLOBALS}; use super::{ @@ -17,21 +17,23 @@ use super::{ /// Height of the list view (width always max. available) const LIST_VIEW_HEIGHT: f32 = 60.0; /// Height of the list view (width always max. available) -const DETAIL_VIEW_HEIGHT: f32 = 80.0; +const DETAIL_VIEW_HEIGHT: f32 = 90.0; /// Height of the edit view (width always max. available) -const EDIT_VIEW_HEIGHT: f32 = 280.0; +const EDIT_VIEW_HEIGHT: f32 = 270.0; /// Height required for one auth-permission drop-down const EDIT_VIEW_AUTH_PERM_HEIGHT: f32 = 25.0; /// Y-offset for first separator -const HLINE_1_Y_OFFSET: f32 = LIST_VIEW_HEIGHT - 12.0; +const HLINE_1_Y_OFFSET: f32 = LIST_VIEW_HEIGHT; /// Y-offset for second separator -const HLINE_2_Y_OFFSET: f32 = 210.0; +const HLINE_2_Y_OFFSET: f32 = 200.0; /// Y top for the detail section -const DETAIL_SECTION_TOP: f32 = TEXT_TOP + LIST_VIEW_HEIGHT; +const DETAIL_SECTION_TOP: f32 = TEXT_TOP + LIST_VIEW_HEIGHT + 20.0; +/// Space needed for rank adjuster +const RANK_SWITCH_HEIGHT: f32 = 30.0; /// Size of edit button const EDIT_BTN_SIZE: f32 = 20.0; /// Spacing of stats row to heading -const STATS_Y_SPACING: f32 = 1.5 * TITLE_FONT_SIZE; +const STATS_Y_SPACING: f32 = 2.0 * TITLE_FONT_SIZE; /// Distance of usage switch-left from TEXT_RIGHT const USAGE_SWITCH_PULL_RIGHT: f32 = 300.0; /// Spacing of usage switches: y direction @@ -288,19 +290,18 @@ impl RelayEntry { } impl RelayEntry { - fn paint_title(&self, ui: &mut Ui, rect: &Rect) { - let title = self.relay.url.as_str().to_owned(); - let text = RichText::new(title).size(list_entry::TITLE_FONT_SIZE); - let galley = list_entry::text_to_galley_max_width( - ui, - text.into(), - Align::LEFT, - rect.width() - 200.0, - ); + fn paint_title(&self, ui: &mut Ui, theme: &Theme, rect: &Rect) { let pos = rect.min + vec2(TEXT_LEFT + STATUS_SYMBOL_SPACE, TEXT_TOP); - let rect = draw_text_galley_at(ui, pos, galley, Some(self.accent), None); - ui.interact(rect, ui.next_auto_id(), Sense::hover()) - .on_hover_text(self.relay.url.as_str()); + super::relay_url_at( + ui, + theme, + pos, + rect.width() - 220.0, + &self.relay.url, + Some(list_entry::TITLE_FONT_SIZE), + true, + ) + .on_hover_text(self.relay.url.as_str().to_owned()); // paint status indicator // green - connected @@ -336,7 +337,7 @@ impl RelayEntry { let rect = draw_text_at(ui, pos, symbol.into(), Align::LEFT, Some(color), None); // set tooltip - ui.interact(rect, ui.next_auto_id(), Sense::hover()) + ui.interact(rect, ui.next_auto_id().with("ind"), Sense::hover()) .on_hover_text(tooltip); } @@ -560,7 +561,7 @@ impl RelayEntry { response.on_hover_text("The relay is not configured and either has low usage, poor success, or you have disabled it."); } - fn paint_usage(&self, ui: &mut Ui, rect: &Rect) { + fn paint_usage(&self, ui: &mut Ui, rect: &Rect, mut response: Response) -> Response { const RIGHT: f32 = -17.0; const SPACE: f32 = 23.0; @@ -593,16 +594,20 @@ impl RelayEntry { // ---- Read ---- let pos = right + vec2(RIGHT - 6.0 * SPACE, 0.0); let (text, color) = switch(ui, "R", self.usage.read); - let (galley, response) = allocate_text_at(ui, pos, text.into(), align, self.make_id("R")); + let (galley, resp) = allocate_text_at(ui, pos, text.into(), align, self.make_id("R")); draw_text_galley_at(ui, pos, galley, Some(color), None); - response.on_hover_text(READ_HOVER_TEXT); + response |= resp + .on_hover_text(READ_HOVER_TEXT) + .on_hover_cursor(CursorIcon::PointingHand); // ---- Inbox ---- let pos = right + vec2(RIGHT - 5.0 * SPACE, 0.0); let (text, color) = switch(ui, "I", self.usage.inbox); - let (galley, response) = allocate_text_at(ui, pos, text.into(), align, self.make_id("I")); + let (galley, resp) = allocate_text_at(ui, pos, text.into(), align, self.make_id("I")); draw_text_galley_at(ui, pos, galley, Some(color), None); - response.on_hover_text(INBOX_HOVER_TEXT); + response |= resp + .on_hover_text(INBOX_HOVER_TEXT) + .on_hover_cursor(CursorIcon::PointingHand); // ---- + ---- let pos = pos - vec2(SPACE / 2.0, 0.0); @@ -611,16 +616,20 @@ impl RelayEntry { // ---- Write ---- let pos = right + vec2(RIGHT - 4.0 * SPACE, 0.0); let (text, color) = switch(ui, "W", self.usage.write); - let (galley, response) = allocate_text_at(ui, pos, text.into(), align, self.make_id("W")); + let (galley, resp) = allocate_text_at(ui, pos, text.into(), align, self.make_id("W")); draw_text_galley_at(ui, pos, galley, Some(color), None); - response.on_hover_text(WRITE_HOVER_TEXT); + response |= resp + .on_hover_text(WRITE_HOVER_TEXT) + .on_hover_cursor(CursorIcon::PointingHand); // ---- Outbox ---- let pos = right + vec2(RIGHT - 3.0 * SPACE, 0.0); let (text, color) = switch(ui, "O", self.usage.outbox); - let (galley, response) = allocate_text_at(ui, pos, text.into(), align, self.make_id("O")); + let (galley, resp) = allocate_text_at(ui, pos, text.into(), align, self.make_id("O")); draw_text_galley_at(ui, pos, galley, Some(color), None); - response.on_hover_text(OUTBOX_HOVER_TEXT); + response |= resp + .on_hover_text(OUTBOX_HOVER_TEXT) + .on_hover_cursor(CursorIcon::PointingHand); // ---- + ---- let pos = pos - vec2(SPACE / 2.0, 0.0); @@ -629,29 +638,37 @@ impl RelayEntry { // ---- Discover ---- let pos = right + vec2(RIGHT - 2.0 * SPACE, 0.0); let (text, color) = switch(ui, "D", self.usage.discover); - let (galley, response) = allocate_text_at(ui, pos, text.into(), align, self.make_id("D")); + let (galley, resp) = allocate_text_at(ui, pos, text.into(), align, self.make_id("D")); draw_text_galley_at(ui, pos, galley, Some(color), None); - response.on_hover_text(DISCOVER_HOVER_TEXT); + response |= resp + .on_hover_text(DISCOVER_HOVER_TEXT) + .on_hover_cursor(CursorIcon::PointingHand); // ---- Spamsafe ---- let pos = right + vec2(RIGHT - 1.0 * SPACE, 0.0); let (text, color) = switch(ui, "S", self.usage.spamsafe); - let (galley, response) = allocate_text_at(ui, pos, text.into(), align, self.make_id("S")); + let (galley, resp) = allocate_text_at(ui, pos, text.into(), align, self.make_id("S")); draw_text_galley_at(ui, pos, galley, Some(color), None); - response.on_hover_text(SPAMSAFE_HOVER_TEXT); + response |= resp + .on_hover_text(SPAMSAFE_HOVER_TEXT) + .on_hover_cursor(CursorIcon::PointingHand); // ---- DM ---- let pos = right + vec2(RIGHT - 0.0 * SPACE, 0.0); let (text, color) = switch(ui, "DM", self.usage.dm); - let (galley, response) = allocate_text_at(ui, pos, text.into(), align, self.make_id("DM")); + let (galley, resp) = allocate_text_at(ui, pos, text.into(), align, self.make_id("DM")); draw_text_galley_at(ui, pos, galley, Some(color), None); - response.on_hover_text(DM_USE_HOVER_TEXT); + response |= resp + .on_hover_text(DM_USE_HOVER_TEXT) + .on_hover_cursor(CursorIcon::PointingHand); + + response } fn paint_nip11(&self, ui: &mut Ui, rect: &Rect) { let align = egui::Align::LEFT; let max_width = rect.width() - TEXT_RIGHT - TEXT_LEFT - USAGE_SWITCH_PULL_RIGHT - 30.0; - let pos = rect.left_top() + vec2(TEXT_LEFT, DETAIL_SECTION_TOP); + let pos = rect.left_top() + vec2(TEXT_LEFT, DETAIL_SECTION_TOP + RANK_SWITCH_HEIGHT); if let Some(doc) = &self.relay.nip11 { if let Some(contact) = &doc.contact { let rect = draw_text_at(ui, pos, contact.into(), align, None, None); @@ -1068,104 +1085,104 @@ impl RelayEntry { None, ); } - let pos = pos + vec2(0.0, USAGE_SWITCH_Y_SPACING); - { - // ---- rank ---- - let r = self.relay.rank; - let mut new_r = self.relay.rank; - let txt_color = ui.visuals().text_color(); - let on_text = ui.visuals().extreme_bg_color; - let btn_height: f32 = ui.spacing().interact_size.y; - let btn_round: Rounding = Rounding::same(btn_height / 2.0); - let font: FontId = Default::default(); + } - let pos = pos + vec2(USAGE_SWITCH_X_SPACING, 0.0); + pub fn paint_rank_setting(&mut self, ui: &mut Ui, rect: &Rect) { + // rank switch is painted above NIP-11 section + let r = self.relay.rank; + let mut new_r = self.relay.rank; + let txt_color = ui.visuals().text_color(); + let on_text = ui.visuals().extreme_bg_color; + let off_fill_color = ui.visuals().widgets.inactive.bg_fill; + let btn_height: f32 = ui.spacing().interact_size.y; + let btn_round: Rounding = Rounding::same(btn_height / 2.0); + let font: FontId = Default::default(); + + let pos = rect.left_top() + vec2(TEXT_LEFT, DETAIL_SECTION_TOP); + + let label_rect = draw_text_at( + ui, + pos, + "Relay-picker rank:".into(), + Align::LEFT, + Some(txt_color), + None, + ); + + let pos = pos + vec2(5.0 + label_rect.width(), 0.0); + { + // -- value display -- + let rect = + Rect::from_min_size(pos + vec2(10.0, -4.0), vec2(40.0 + 8.0, btn_height + 4.0)); + ui.painter().rect( + rect, + btn_round, + ui.visuals().extreme_bg_color, + Stroke::new(1.0, off_fill_color), + ); + ui.painter().text( + pos + vec2(34.0, 0.0), + Align2::CENTER_TOP, + format!("{}", r), + font.clone(), + txt_color, + ); { - draw_text_at( - ui, - pos - vec2(5.0, 0.0), - "Relay-picker rank:".into(), - Align::RIGHT, - Some(txt_color), - None, + // -- - button -- + let rect = Rect::from_min_size(pos + vec2(0.0, -2.0), vec2(btn_height, btn_height)); + let resp = ui + .interact(rect, self.make_id("rank_sub"), Sense::click()) + .on_hover_cursor(CursorIcon::PointingHand); + if resp.clicked() { + new_r = new_r.saturating_sub(1) + } + let (fill, txt) = if resp.hovered() { + (self.accent_hover, on_text) + } else { + (self.accent, on_text) + }; + ui.painter().rect(rect, btn_round, fill, Stroke::NONE); + ui.painter().text( + rect.center(), + Align2::CENTER_CENTER, + "\u{2212}", + font.clone(), + txt, ); } - { - // -- value display -- + // -- + button -- let rect = - Rect::from_min_size(pos + vec2(10.0, -4.0), vec2(40.0 + 8.0, btn_height + 4.0)); - ui.painter().rect( - rect, - btn_round, - ui.visuals().extreme_bg_color, - Stroke::new(1.0, off_fill_color), - ); + Rect::from_min_size(pos + vec2(48.0, -2.0), vec2(btn_height, btn_height)); + let resp = ui + .interact(rect, self.make_id("rank_add"), Sense::click()) + .on_hover_cursor(CursorIcon::PointingHand); + if resp.clicked() { + if new_r < 9 { + new_r += 1; + } + } + let (fill, txt) = if resp.hovered() { + (self.accent_hover, on_text) + } else { + (self.accent, on_text) + }; + ui.painter().rect(rect, btn_round, fill, Stroke::NONE); ui.painter().text( - pos + vec2(34.0, 0.0), - Align2::CENTER_TOP, - format!("{}", r), + rect.center(), + Align2::CENTER_CENTER, + "\u{002B}", font.clone(), - txt_color, + txt, ); - { - // -- - button -- - let rect = - Rect::from_min_size(pos + vec2(0.0, -2.0), vec2(btn_height, btn_height)); - let resp = ui - .interact(rect, self.make_id("rank_sub"), Sense::click()) - .on_hover_cursor(CursorIcon::PointingHand); - if resp.clicked() { - new_r = new_r.saturating_sub(1) - } - let (fill, txt) = if resp.hovered() { - (self.accent_hover, on_text) - } else { - (self.accent, on_text) - }; - ui.painter().rect(rect, btn_round, fill, Stroke::NONE); - ui.painter().text( - rect.center(), - Align2::CENTER_CENTER, - "\u{2212}", - font.clone(), - txt, - ); - } - { - // -- + button -- - let rect = - Rect::from_min_size(pos + vec2(48.0, -2.0), vec2(btn_height, btn_height)); - let resp = ui - .interact(rect, self.make_id("rank_add"), Sense::click()) - .on_hover_cursor(CursorIcon::PointingHand); - if resp.clicked() { - if new_r < 9 { - new_r += 1; - } - } - let (fill, txt) = if resp.hovered() { - (self.accent_hover, on_text) - } else { - (self.accent, on_text) - }; - ui.painter().rect(rect, btn_round, fill, Stroke::NONE); - ui.painter().text( - rect.center(), - Align2::CENTER_CENTER, - "\u{002B}", - font.clone(), - txt, - ); - } } + } - if new_r != self.relay.rank { - let _ = GLOBALS.to_overlord.send(ToOverlordMessage::RankRelay( - self.relay.url.clone(), - new_r as u8, - )); - } + if new_r != self.relay.rank { + let _ = GLOBALS.to_overlord.send(ToOverlordMessage::RankRelay( + self.relay.url.clone(), + new_r as u8, + )); } } @@ -1207,7 +1224,7 @@ impl RelayEntry { } fn interact_bg_color(&self, response: &Response) -> Option { - if response.hovered() { + if response.contains_pointer() { Some(self.bg_hover) } else { Some(self.bg_fill) @@ -1215,16 +1232,16 @@ impl RelayEntry { } /// Do layout and position the galley in the ui, without painting it or adding widget info. - fn update_list_view(self, ui: &mut Ui) -> Response { + fn update_list_view(self, ui: &mut Ui, theme: &Theme) -> Response { let (rect, mut response) = list_entry::allocate_space(ui, LIST_VIEW_HEIGHT); response = response.on_hover_cursor(egui::CursorIcon::PointingHand); // all the heavy lifting is only done if it's actually visible if ui.is_rect_visible(rect) { list_entry::paint_frame(ui, &rect, self.interact_bg_color(&response)); - self.paint_title(ui, &rect); + self.paint_title(ui, theme, &rect); if self.relay.has_any_usage_bit() || self.relay.is_good_for_advertise() { - self.paint_usage(ui, &rect); + response = self.paint_usage(ui, &rect, response); } else { self.paint_low_quality(ui, &rect); } @@ -1234,17 +1251,17 @@ impl RelayEntry { response } - fn update_detail_view(self, ui: &mut Ui) -> Response { + fn update_detail_view(self, ui: &mut Ui, theme: &Theme) -> Response { let (rect, mut response) = list_entry::allocate_space(ui, DETAIL_VIEW_HEIGHT); response = response.on_hover_cursor(egui::CursorIcon::PointingHand); // all the heavy lifting is only done if it's actually visible if ui.is_rect_visible(rect) { list_entry::paint_frame(ui, &rect, self.interact_bg_color(&response)); - self.paint_title(ui, &rect); + self.paint_title(ui, theme, &rect); self.paint_stats(ui, &rect); if self.relay.has_any_usage_bit() || self.relay.is_good_for_advertise() { - self.paint_usage(ui, &rect); + response = self.paint_usage(ui, &rect, response); } self.paint_reasons(ui, &rect); } @@ -1252,7 +1269,7 @@ impl RelayEntry { response } - fn update_edit_view(mut self, ui: &mut Ui) -> Response { + fn update_edit_view(mut self, ui: &mut Ui, theme: &Theme) -> Response { let (height, hline2_offset) = match (self.auth_require_permission, self.conn_require_permission) { (true, true) => ( @@ -1267,21 +1284,28 @@ impl RelayEntry { }; let size = vec2(ui.available_width(), height); - let rect = Rect::from_min_size(ui.next_widget_position(), size); - - let mut response = ui.interact(rect, self.make_id("frame"), egui::Sense::hover()); + let pos = ui.next_widget_position(); + let no_response_rect = Rect::from_min_size(pos, vec2(1.0, 1.0)); + let rect = Rect::from_min_size(pos, size); + + let mut response = ui.interact( + no_response_rect, + self.make_id("frame"), + egui::Sense::hover(), + ); // all the heavy lifting is only done if it's actually visible if ui.is_visible() { list_entry::paint_frame(ui, &rect, Some(self.bg_fill)); - self.paint_title(ui, &rect); + self.paint_title(ui, theme, &rect); self.paint_stats(ui, &rect); paint_hline(ui, &rect, HLINE_1_Y_OFFSET); + self.paint_rank_setting(ui, &rect); self.paint_nip11(ui, &rect); self.paint_usage_settings(ui, &rect); self.paint_permissions(ui, &rect); paint_hline(ui, &rect, hline2_offset); - response |= self.paint_lower_buttons(ui, &rect); + response = self.paint_lower_buttons(ui, &rect); response |= self.paint_close_btn(ui, &rect); } @@ -1291,16 +1315,14 @@ impl RelayEntry { response } -} -impl Widget for RelayEntry { - fn ui(self, ui: &mut Ui) -> Response { + pub fn show(self, ui: &mut Ui, theme: &Theme) -> Response { ui.visuals_mut().widgets.hovered.fg_stroke.color = self.accent; match self.view { - RelayEntryView::List => self.update_list_view(ui), - RelayEntryView::Detail => self.update_detail_view(ui), - RelayEntryView::Edit => self.update_edit_view(ui), + RelayEntryView::List => self.update_list_view(ui, theme), + RelayEntryView::Detail => self.update_detail_view(ui, theme), + RelayEntryView::Edit => self.update_edit_view(ui, theme), } } }