Merge pull request #817 from bu5hm4nn/feature/egui-upgrade

egui upgrade to 0.28.2
This commit is contained in:
Michael Dilger 2024-07-23 09:53:58 +00:00 committed by GitHub
commit 2d9b7b5fbd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 505 additions and 340 deletions

485
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ resolver = "2"
[patch.crates-io]
# override egui crate for egui_video dependency
egui = { git = "https://github.com/bu5hm4nn/egui", rev = "63dde4c9b311da0cae0cb9f9465bf7273227be6c" }
egui = { git = "https://github.com/bu5hm4nn/egui", rev = "521b087269acb6032d5040fa8bad33e84dbadede" }
# Use the master branch of SDL2 to include a fix related to clang (and XCode after 14.2)
sdl2 = { git = "https://github.com/Rust-SDL2/rust-sdl2", rev = "f2f1e29a416bcc22f2faf411866db2c8d9536308" }

View File

@ -20,11 +20,11 @@ appimage = [ "gossip-lib/appimage" ]
[dependencies]
bech32 = "0.11"
eframe = { git = "https://github.com/bu5hm4nn/egui", rev = "63dde4c9b311da0cae0cb9f9465bf7273227be6c", features = [ "persistence", "wayland", "wgpu" ] }
egui-winit = { git = "https://github.com/bu5hm4nn/egui", rev = "63dde4c9b311da0cae0cb9f9465bf7273227be6c", features = [ "default" ] }
egui_extras = { git = "https://github.com/bu5hm4nn/egui", rev = "63dde4c9b311da0cae0cb9f9465bf7273227be6c", features = [ "syntect" ] }
#egui = { git = "https://github.com/bu5hm4nn/egui", rev = "63dde4c9b311da0cae0cb9f9465bf7273227be6c", features = [ "deadlock_detection" ] }
egui-video = { git = "https://github.com/mikedilger/egui-video", rev = "97f58f88dfe912697393567830d0751676492a89", features = [ "from_bytes" ], optional = true }
eframe = { git = "https://github.com/bu5hm4nn/egui", rev = "521b087269acb6032d5040fa8bad33e84dbadede", features = [ "persistence", "wayland", "wgpu" ] }
egui-winit = { git = "https://github.com/bu5hm4nn/egui", rev = "521b087269acb6032d5040fa8bad33e84dbadede", features = [ "default" ] }
egui_extras = { git = "https://github.com/bu5hm4nn/egui", rev = "521b087269acb6032d5040fa8bad33e84dbadede", features = [ "syntect" ] }
## egui = { git = "https://github.com/bu5hm4nn/egui", rev = "521b087269acb6032d5040fa8bad33e84dbadede", features = [ "deadlock_detection" ] }
egui-video = { git = "https://github.com/bu5hm4nn/egui-video", rev = "21005df83139d0a7bdc910c66df3bc5a7b7bade2", features = [ "from_bytes" ], optional = true }
gossip-relay-picker = { git = "https://github.com/mikedilger/gossip-relay-picker", rev = "3ea9ccfc641cdef1574a3c054a086ac2e24d3c4a" }
gossip-lib = { path = "../gossip-lib" }
humansize = "2.1"

View File

@ -160,7 +160,16 @@ pub(super) fn render_note(
}
// Mark post as viewed if hovered AND we are not scrolling
if !viewed && inner_response.response.hovered() && !app.is_scrolling() {
if !viewed
&& ui
.interact(
inner_response.response.rect,
ui.next_auto_id().with("hov"),
egui::Sense::hover(),
)
.hovered()
&& !app.is_scrolling()
{
let _ = GLOBALS.storage.mark_event_viewed(id, None);
}
@ -1305,7 +1314,7 @@ fn note_actions(
.show(ui);
let menu = widgets::MoreMenu::simple(ui.auto_id_with(note.event.id))
.with_min_size(vec2(100.0, 0.0))
.with_max_size(vec2(140.0, f32::INFINITY));
.with_max_size(vec2(140.0, ui.ctx().available_rect().height()));
let mut items: Vec<MoreMenuItem> = Vec::new();
// ---- Copy Text ----
@ -1647,7 +1656,7 @@ fn draw_seen_on(app: &mut GossipUi, ui: &mut Ui, note: &std::cell::Ref<NoteData>
let response = ui.add(Label::new(RichText::new("👁").size(12.0)).sense(Sense::hover()));
if response.hovered() {
egui::Area::new(ui.next_auto_id())
egui::Area::new(ui.next_auto_id().with("seen_on"))
.movable(false)
.interactable(false)
// .pivot(Align2::RIGHT_TOP) // Fails to work as advertised

View File

@ -137,7 +137,7 @@ pub fn run() -> Result<(), Error> {
if let Err(e) = eframe::run_native(
"gossip",
options,
Box::new(|cc| Box::new(GossipUi::new(cc))),
Box::new(|cc| Ok(Box::new(GossipUi::new(cc)))),
) {
tracing::error!("Eframe error: {}", e);
}
@ -1310,7 +1310,7 @@ impl GossipUi {
+ Vec2::new(-crate::AVATAR_SIZE_F32 * 2.0, -crate::AVATAR_SIZE_F32 * 2.0)
};
egui::Area::new(ui.next_auto_id())
egui::Area::new(ui.next_auto_id().with("plus"))
.movable(false)
.interactable(true)
.fixed_pos(pos)
@ -1656,7 +1656,9 @@ impl GossipUi {
fn begin_ui(&self, ui: &mut Ui) {
// if a dialog is open, disable the rest of the UI
ui.set_enabled(self.enable_ui());
if !self.enable_ui() {
ui.disable();
}
}
pub fn richtext_from_person_nip05(person: &Person) -> RichText {
@ -2016,7 +2018,8 @@ impl GossipUi {
fn open_menu(&mut self, ctx: &Context, item: SubMenu) {
for (submenu, id) in self.submenu_ids.iter() {
let mut cstate = egui::CollapsingState::load_with_default_open(ctx, *id, false);
let mut cstate =
egui::collapsing_header::CollapsingState::load_with_default_open(ctx, *id, false);
if item == SubMenu::Feeds || *submenu != SubMenu::Feeds {
cstate.set_open(*submenu == item);
}
@ -2026,7 +2029,8 @@ impl GossipUi {
fn close_all_menus_except_feeds(&mut self, ctx: &Context) {
for (submenu, id) in self.submenu_ids.iter() {
let mut cstate = egui::CollapsingState::load_with_default_open(ctx, *id, false);
let mut cstate =
egui::collapsing_header::CollapsingState::load_with_default_open(ctx, *id, false);
if *submenu != SubMenu::Feeds {
cstate.set_open(false);
}
@ -2039,9 +2043,12 @@ impl GossipUi {
ui: &mut Ui,
ctx: &Context,
submenu: SubMenu,
) -> (egui::CollapsingState, Response) {
let mut cstate =
egui::CollapsingState::load_with_default_open(ctx, self.submenu_ids[&submenu], false);
) -> (egui::collapsing_header::CollapsingState, Response) {
let mut cstate = egui::collapsing_header::CollapsingState::load_with_default_open(
ctx,
self.submenu_ids[&submenu],
false,
);
let open = cstate.is_open();
let txt = if open {
submenu.to_string() + " \u{25BE}"
@ -2072,7 +2079,7 @@ impl GossipUi {
(cstate, header_res.response)
}
fn after_openable_menu(&self, ui: &mut Ui, cstate: &egui::CollapsingState) {
fn after_openable_menu(&self, ui: &mut Ui, cstate: &egui::collapsing_header::CollapsingState) {
if cstate.is_open() {
ui.add_space(10.0)
}

View File

@ -134,7 +134,9 @@ pub(super) fn update(
});
});
ui.set_enabled(enabled);
if !enabled {
ui.disable();
}
ui.add_space(5.0);
@ -303,7 +305,10 @@ pub(super) fn update(
let menu =
widgets::MoreMenu::bubble(ui.auto_id_with(person.pubkey))
.with_min_size(vec2(100.0, 0.0))
.with_max_size(vec2(100.0, f32::INFINITY));
.with_max_size(vec2(
100.0,
ctx.available_rect().height(),
));
let mut items: Vec<MoreMenuItem> = Vec::new();
// actions
@ -779,7 +784,7 @@ pub(super) fn render_more_list_actions(
let menu = widgets::MoreMenu::bubble(ui.next_auto_id())
.with_min_size(vec2(100.0, 0.0))
.with_max_size(vec2(140.0, f32::INFINITY));
.with_max_size(vec2(140.0, ui.ctx().available_rect().height()));
let mut items: Vec<MoreMenuItem> = Vec::new();
items.push(MoreMenuItem::Button(MoreMenuButton::new(

View File

@ -33,7 +33,9 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
});
});
ui.set_enabled(enabled);
if !enabled {
ui.disable();
}
let mut all_lists = GLOBALS
.storage
@ -109,13 +111,10 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
},
);
if row_response
.response
.inner
.interact(Sense::click())
.on_hover_cursor(egui::CursorIcon::PointingHand)
.clicked()
|| row_response
.inner
.on_hover_cursor(egui::CursorIcon::PointingHand)
.clicked()
{
app.set_page(ctx, Page::PeopleList(list));
}

View File

@ -4,6 +4,7 @@ use crate::ui::widgets::list_entry;
use crate::ui::widgets::CopyButton;
use crate::AVATAR_SIZE_F32;
use eframe::egui::{self, Label};
use eframe::epaint::PathStroke;
use egui::{Context, RichText, TextEdit, Ui, Vec2};
use egui_winit::egui::vec2;
use egui_winit::egui::InnerResponse;
@ -461,7 +462,7 @@ fn content(app: &mut GossipUi, ctx: &Context, ui: &mut Ui, pubkey: PublicKey, pe
// Right column, starting with avatar
ui.allocate_ui_with_layout(
vec2(AVATAR_COL_WIDTH, f32::INFINITY),
vec2(AVATAR_COL_WIDTH, ui.ctx().available_rect().height()),
egui::Layout::right_to_left(egui::Align::TOP).with_main_justify(true),
|ui| {
ui.vertical(|ui| {
@ -618,7 +619,7 @@ fn profile_item(
content: impl Into<String>,
) {
let content: String = content.into();
let symbol = CopyButton::new().stroke(egui::Stroke::new(1.4, app.theme.accent_color()));
let symbol = CopyButton::new().stroke(PathStroke::new(1.4, app.theme.accent_color()));
let response = profile_item_frame(ui, width, label, &content, symbol).response;
if response.clicked() {
@ -704,8 +705,8 @@ fn profile_item_frame(
.response
};
let frame_rect = (prepared.frame.inner_margin + prepared.frame.outer_margin)
.expand_rect(prepared.content_ui.min_rect());
let frame_rect = prepared.content_ui.min_rect()
+ (prepared.frame.inner_margin + prepared.frame.outer_margin);
let response = ui
.interact(frame_rect, ui.auto_id_with(&label), egui::Sense::click())

View File

@ -13,7 +13,9 @@ use nostr_types::RelayUrl;
pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
let is_editing = app.relays.edit.is_some();
widgets::page_header(ui, Page::RelaysActivityMonitor.name(), |ui| {
ui.set_enabled(!is_editing);
if is_editing {
ui.disable();
}
super::configure_list_btn(app, ui);
btn_h_space!(ui);
super::relay_filter_combo(app, ui);

View File

@ -9,7 +9,9 @@ use gossip_lib::GLOBALS;
pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
let is_editing = app.relays.edit.is_some();
widgets::page_header(ui, Page::RelaysKnownNetwork(None).name(), |ui| {
ui.set_enabled(!is_editing);
if is_editing {
ui.disable();
}
super::configure_list_btn(app, ui);
btn_h_space!(ui);
super::relay_filter_combo(app, ui);

View File

@ -11,7 +11,9 @@ 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();
widgets::page_header(ui, Page::RelaysMine.name(), |ui| {
ui.set_enabled(!is_editing);
if is_editing {
ui.disable();
}
super::configure_list_btn(app, ui);
btn_h_space!(ui);
super::relay_filter_combo(app, ui);

View File

@ -293,7 +293,7 @@ pub(super) fn stop_entry_dialog(app: &mut GossipUi) {
pub(super) fn entry_dialog(ctx: &Context, app: &mut GossipUi) {
let dlg_size = vec2(ctx.screen_rect().width() * 0.66, 120.0);
egui::Area::new("hide-background-area")
egui::Area::new(Id::new("hide-background-area"))
.fixed_pos(ctx.screen_rect().left_top())
.movable(false)
.interactable(false)
@ -313,11 +313,6 @@ pub(super) fn entry_dialog(ctx: &Context, app: &mut GossipUi) {
.interactable(true)
.order(egui::Order::Foreground)
.fixed_pos(ctx.screen_rect().center() - vec2(dlg_size.x / 2.0, dlg_size.y));
area.show_open_close_animation(
ctx,
&frame,
app.relays.add_dialog_step != AddRelayDialogStep::Inactive,
);
area.show(ctx, |ui| {
frame.fill = ui.visuals().extreme_bg_color;
frame.inner_margin = egui::Margin::symmetric(20.0, 10.0);

View File

@ -1,6 +1,6 @@
use super::{NoteRenderData, ThemeDef};
use crate::ui::HighlightType;
use eframe::egui::style::{Selection, WidgetVisuals, Widgets};
use eframe::egui::style::{Selection, TextCursorStyle, WidgetVisuals, Widgets};
use eframe::egui::{
vec2, FontDefinitions, Margin, Pos2, RichText, Shape, Stroke, Style, TextFormat, TextStyle,
Vec2, Visuals,
@ -28,14 +28,18 @@ pub trait ShadowBuilder {
impl ShadowBuilder for Shadow {
fn soft_dark() -> Self {
Self {
extrusion: 30.0,
offset: vec2(6.0, 10.0),
blur: 30.0,
spread: 0.0,
color: Color32::from_black_alpha(40),
}
}
fn soft_light() -> Self {
Self {
extrusion: 30.0,
offset: vec2(6.0, 10.0),
blur: 30.0,
spread: 0.0,
color: Color32::from_black_alpha(10),
}
}
@ -284,8 +288,8 @@ impl ThemeDef for DefaultTheme {
stroke: Stroke::new(0.0, Color32::from_gray(220)),
},
window_shadow: Shadow::big_dark(),
popup_shadow: Shadow::soft_dark(),
window_shadow: Visuals::dark().window_shadow,
popup_shadow: Visuals::dark().popup_shadow,
indent_has_left_vline: false,
menu_rounding: Rounding::same(2.0),
@ -295,8 +299,12 @@ impl ThemeDef for DefaultTheme {
window_rounding: Rounding::same(6.0),
window_highlight_topmost: false,
resize_corner_size: 12.0,
text_cursor: Stroke::new(2.0, Color32::from_rgb(192, 222, 255)),
text_cursor_preview: false,
text_cursor: TextCursorStyle {
stroke: Stroke::new(2.0, Color32::from_rgb(192, 222, 255)),
preview: false,
..Default::default()
},
clip_rect_margin: 3.0, // should be at least half the size of the widest frame stroke + max WidgetVisuals::expansion
button_frame: true,
collapsing_header_frame: false,
@ -372,8 +380,8 @@ impl ThemeDef for DefaultTheme {
stroke: Stroke::new(1.0, Color32::from_gray(40)), // DONE
},
window_shadow: Shadow::big_light(),
popup_shadow: Shadow::soft_light(),
window_shadow: Visuals::light().window_shadow,
popup_shadow: Visuals::light().popup_shadow,
indent_has_left_vline: false,
menu_rounding: Rounding::same(2.0),
@ -383,8 +391,11 @@ impl ThemeDef for DefaultTheme {
window_rounding: Rounding::same(6.0),
window_highlight_topmost: false,
resize_corner_size: 12.0,
text_cursor: Stroke::new(2.0, Color32::from_rgb(0, 83, 125)),
text_cursor_preview: false,
text_cursor: TextCursorStyle {
stroke: Stroke::new(2.0, Color32::from_rgb(0, 83, 125)),
preview: false,
..Default::default()
},
clip_rect_margin: 3.0, // should be at least half the size of the widest frame stroke + max WidgetVisuals::expansion
button_frame: true,
collapsing_header_frame: false,

View File

@ -414,7 +414,7 @@ fn textedit_test(app: &mut GossipUi, ui: &mut Ui) {
ui.add_space(20.0);
ui.horizontal(|ui| {
ui.add_sized(CSIZE, egui::Label::new("Disabled"));
ui.set_enabled(false);
ui.disable();
ui.add_space(20.0);
ui.vertical(|ui| {
widgets::TextEdit::singleline(theme, &mut app.theme_test.textedit_empty)
@ -477,7 +477,7 @@ fn switch_test(app: &mut GossipUi, ui: &mut Ui) {
ui.add_space(20.0);
ui.horizontal(|ui| {
ui.add_sized(CSIZE, egui::Label::new("Disabled"));
ui.set_enabled(false);
ui.disable();
ui.add_space(20.0);
ui.vertical(|ui| {
ui.horizontal(|ui| {

View File

@ -234,7 +234,7 @@ impl Button<'_> {
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click());
response.widget_info(|| {
if let Some(text) = text {
WidgetInfo::labeled(WidgetType::Button, text.text())
WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), text.text())
} else {
WidgetInfo::new(WidgetType::Button)
}

View File

@ -39,7 +39,7 @@ pub(in crate::ui) fn show_contact_search(
let frame = egui::Frame::popup(ui.style())
.rounding(egui::Rounding::ZERO)
.inner_margin(egui::Margin::same(0.0));
let area = egui::Area::new(ui.next_auto_id())
let area = egui::Area::new(ui.next_auto_id().with("tt"))
.pivot(pivot)
.fixed_pos(fixed_pos)
.movable(false)
@ -74,9 +74,8 @@ pub(in crate::ui) fn show_contact_search(
prepared.content_ui.set_max_width(super::TAGG_WIDTH);
prepared.content_ui.set_min_height(27.0);
let frame_rect = (prepared.frame.inner_margin
+ prepared.frame.outer_margin)
.expand_rect(prepared.content_ui.min_rect());
let frame_rect = prepared.content_ui.min_rect()
+ (prepared.frame.inner_margin + prepared.frame.outer_margin);
let response = ui
.interact(
@ -160,8 +159,6 @@ pub(in crate::ui) fn show_contact_search(
});
});
}
area.show_open_close_animation(ui.ctx(), &frame, is_open);
}
pub(in crate::ui) fn capture_keyboard_for_search(

View File

@ -1,11 +1,14 @@
use eframe::{egui, epaint};
use eframe::{
egui,
epaint::{self, ColorMode, PathStroke},
};
use egui::{Color32, Pos2, Response, Sense, Shape, Ui, Vec2, Widget};
use epaint::{PathShape, Stroke};
use epaint::PathShape;
pub const COPY_SYMBOL_SIZE: Vec2 = Vec2::new(12.0, 12.0);
pub struct CopyButton {
stroke: Option<Stroke>,
stroke: Option<PathStroke>,
}
impl CopyButton {
@ -13,7 +16,7 @@ impl CopyButton {
Self { stroke: None }
}
pub(crate) fn stroke(mut self, stroke: Stroke) -> Self {
pub(crate) fn stroke(mut self, stroke: PathStroke) -> Self {
self.stroke = Some(stroke);
self
}
@ -44,10 +47,14 @@ impl CopyButton {
],
closed: false,
fill: Color32::TRANSPARENT,
stroke: self.stroke.unwrap_or(Stroke {
width: 1.0,
color: Color32::from_rgb(0x8d, 0x7f, 0x73),
}),
stroke: if let Some(stroke) = &self.stroke {
stroke.clone()
} else {
PathStroke {
width: 1.0,
color: ColorMode::Solid(Color32::from_rgb(0x8d, 0x7f, 0x73)),
}
},
}));
ui.painter().add(Shape::Path(PathShape {
@ -75,10 +82,14 @@ impl CopyButton {
],
closed: true,
fill: Color32::TRANSPARENT,
stroke: self.stroke.unwrap_or(Stroke {
width: 1.0,
color: Color32::from_rgb(0x8d, 0x7f, 0x73),
}),
stroke: if let Some(stroke) = &self.stroke {
stroke.clone()
} else {
PathStroke {
width: 1.0,
color: ColorMode::Solid(Color32::from_rgb(0x8d, 0x7f, 0x73)),
}
},
}));
}
}

View File

@ -43,14 +43,12 @@ pub(crate) fn paint_frame(ui: &mut Ui, rect: &Rect, fill: Option<Color32>) {
rect.max - vec2(OUTER_MARGIN_RIGHT, OUTER_MARGIN_BOTTOM),
);
let fill = fill.unwrap_or(ui.visuals().extreme_bg_color);
ui.painter().add(epaint::RectShape {
rect: frame_rect,
rounding: Rounding::same(5.0),
ui.painter().add(epaint::RectShape::new(
frame_rect,
Rounding::same(5.0),
fill,
stroke: Stroke::NONE,
fill_texture_id: Default::default(),
uv: Rect::ZERO,
});
Stroke::NONE,
));
}
pub(crate) fn make_frame(ui: &Ui, fill: Option<Color32>) -> Frame {

View File

@ -74,9 +74,6 @@ pub fn modal_popup(
.constrain(true)
.order(egui::Order::Middle)
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]);
area.show_open_close_animation(
ctx, &frame, true, // TODO if we never pass false it won't show a close animation
);
area.show(ctx, |ui| {
if ui.visuals().dark_mode {
frame.fill = ui.visuals().faint_bg_color;
@ -149,9 +146,6 @@ pub fn modal_popup_dyn(
.constrain(true)
.order(egui::Order::Middle)
.anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]);
area.show_open_close_animation(
ctx, &frame, true, // TODO if we never pass false it won't show a close animation
);
let frame_response = area
.show(ctx, |ui| {
if ui.visuals().dark_mode {

View File

@ -66,7 +66,9 @@ impl<'a> MoreMenuButton<'a> {
}
fn show(self, app: &mut GossipUi, ui: &mut Ui) -> Response {
ui.set_enabled(self.enabled);
if !self.enabled {
ui.disable();
}
let response = draw_menu_button(ui, &app.theme, self.text, None);
@ -132,7 +134,9 @@ impl<'a> MoreMenuSubMenu<'a> {
}
fn show(self, app: &mut GossipUi, ui: &mut Ui) -> Response {
ui.set_enabled(self.enabled);
if !self.enabled {
ui.disable();
}
let mut open = load_state(ui, &self.id);
@ -717,7 +721,7 @@ fn draw_menu_button(
// interact
let (rect, response) = ui.allocate_at_least(desired_size, Sense::click());
response.widget_info(|| WidgetInfo::labeled(WidgetType::Button, title.text()));
response.widget_info(|| WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), title.text()));
let state = super::interact_widget_state(ui, &response);
let state = match state {
super::WidgetState::Default => {

View File

@ -16,6 +16,8 @@ use egui::*;
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
pub struct NavItem {
text: WidgetText,
wrap: Option<bool>,
truncate: bool,
sense: Option<Sense>,
color: Option<Color32>,
active_color: Option<Color32>,
@ -27,6 +29,8 @@ impl NavItem {
pub fn new(text: impl Into<WidgetText>, active: bool) -> Self {
Self {
text: text.into(),
wrap: None,
truncate: false,
sense: None,
color: None,
active_color: None,
@ -56,6 +60,42 @@ impl NavItem {
self
}
/// If `true`, the text will wrap to stay within the max width of the [`Ui`].
///
/// Calling `wrap` will override [`Self::truncate`].
///
/// By default [`Self::wrap`] will be `true` in vertical layouts
/// and horizontal layouts with wrapping,
/// and `false` on non-wrapping horizontal layouts.
///
/// Note that any `\n` in the text will always produce a new line.
///
/// You can also use [`crate::Style::wrap`].
#[inline]
#[allow(unused)]
pub fn wrap(mut self, wrap: bool) -> Self {
self.wrap = Some(wrap);
self.truncate = false;
self
}
/// If `true`, the text will stop at the max width of the [`Ui`],
/// and what doesn't fit will be elided, replaced with `…`.
///
/// If the text is truncated, the full text will be shown on hover as a tool-tip.
///
/// Default is `false`, which means the text will expand the parent [`Ui`],
/// or wrap if [`Self::wrap`] is set.
///
/// Calling `truncate` will override [`Self::wrap`].
#[inline]
#[allow(unused)]
pub fn truncate(mut self, truncate: bool) -> Self {
self.wrap = None;
self.truncate = truncate;
self
}
/// Make the label respond to clicks and/or drags.
///
/// By default, a label is inert and does not respond to click or drags.
@ -79,7 +119,15 @@ impl NavItem {
impl NavItem {
/// Do layout and position the galley in the ui, without painting it or adding widget info.
pub fn layout_in_ui(self, ui: &mut Ui) -> (Pos2, Arc<Galley>, Response) {
let sense = self.sense.unwrap_or(Sense::click());
let sense = self.sense.unwrap_or_else(|| {
if ui.memory(|mem| mem.options.screen_reader) {
// We only want to focus labels if the screen reader is on.
Sense::focusable_noninteractive()
} else {
Sense::hover()
}
});
if let WidgetText::Galley(galley) = self.text {
// If the user said "use this specific galley", then just use it:
let (rect, response) = ui.allocate_exact_size(galley.size(), sense);
@ -92,14 +140,18 @@ impl NavItem {
}
let valign = ui.layout().vertical_align();
let mut job = self
let mut layout_job = self
.text
.into_layout_job(ui.style(), FontSelection::Default, valign);
let should_wrap = ui.wrap_text();
let truncate = self.truncate;
let wrap = !truncate
&& self
.wrap
.unwrap_or_else(|| ui.wrap_mode() == TextWrapMode::Wrap);
let available_width = ui.available_width();
if should_wrap
if wrap
&& ui.layout().main_dir() == Direction::LeftToRight
&& ui.layout().main_wrap()
&& available_width.is_finite()
@ -109,29 +161,19 @@ impl NavItem {
let cursor = ui.cursor();
let first_row_indentation = available_width - ui.available_size_before_wrap().x;
egui_assert!(first_row_indentation.is_finite());
debug_assert!(first_row_indentation.is_finite());
job.wrap.max_width = available_width;
job.first_row_min_height = cursor.height();
job.halign = Align::Min;
job.justify = false;
if let Some(first_section) = job.sections.first_mut() {
layout_job.wrap.max_width = available_width;
layout_job.first_row_min_height = cursor.height();
layout_job.halign = Align::Min;
layout_job.justify = false;
if let Some(first_section) = layout_job.sections.first_mut() {
first_section.leading_space = first_row_indentation;
}
let galley = ui.fonts(|f| f.layout_job(job));
let galley = ui.fonts(|fonts| fonts.layout_job(layout_job));
let pos = pos2(ui.max_rect().left(), ui.cursor().top());
assert!(!galley.rows.is_empty(), "Galleys are never empty");
// set the row height to ensure the cursor advancement is correct. when creating a child ui such as with
// ui.horizontal_wrapped, the initial cursor will be set to the height of the child ui. this can lead
// to the cursor not advancing to the second row but rather expanding the height of the cursor.
//
// note that we do not set the row height earlier in this function as we do want to allow populating
// `first_row_min_height` above. however it is crucial the placer knows the actual row height by
// setting the cursor height before ui.allocate_rect() gets called.
ui.set_row_height(galley.rows[0].height());
// collect a response from many rows:
let rect = galley.rows[0].rect.translate(vec2(pos.x, pos.y));
let mut response = ui.allocate_rect(rect, sense);
@ -141,23 +183,27 @@ impl NavItem {
}
(pos, galley, response)
} else {
if should_wrap {
job.wrap.max_width = available_width;
if truncate {
layout_job.wrap.max_width = available_width;
layout_job.wrap.max_rows = 1;
layout_job.wrap.break_anywhere = true;
} else if wrap {
layout_job.wrap.max_width = available_width;
} else {
job.wrap.max_width = f32::INFINITY;
layout_job.wrap.max_width = f32::INFINITY;
};
job.halign = ui.layout().horizontal_placement();
job.justify = ui.layout().horizontal_justify();
layout_job.halign = ui.layout().horizontal_placement();
layout_job.justify = ui.layout().horizontal_justify();
let galley = ui.fonts(|f| f.layout_job(job));
let galley = ui.fonts(|fonts| fonts.layout_job(layout_job));
let (rect, response) = ui.allocate_exact_size(galley.size(), sense);
let pos = match galley.job.halign {
let galley_pos = match galley.job.halign {
Align::LEFT => rect.left_top(),
Align::Center => rect.center_top(),
Align::RIGHT => rect.right_top(),
};
(pos, galley, response)
(galley_pos, galley, response)
}
}
}
@ -169,7 +215,8 @@ impl Widget for NavItem {
let hover_color = self.hover_color;
let active_color = self.active_color;
let (pos, galley, response) = self.layout_in_ui(ui);
response.widget_info(|| WidgetInfo::labeled(WidgetType::Label, galley.text()));
response
.widget_info(|| WidgetInfo::labeled(WidgetType::Label, ui.is_enabled(), galley.text()));
if ui.is_rect_visible(response.rect) {
let color = if hover_color.is_some() && response.hovered() {

View File

@ -357,7 +357,7 @@ impl RelayEntry {
let button_padding = ui.spacing().button_padding;
let galley = WidgetText::from("Close")
.color(ui.visuals().extreme_bg_color)
.into_galley(ui, Some(false), 0.0, TextStyle::Button);
.into_galley(ui, Some(TextWrapMode::Extend), 0.0, TextStyle::Button);
let mut desired_size = galley.size() + 4.0 * button_padding;
desired_size.y = desired_size.y.at_least(ui.spacing().interact_size.y);
let pos = rect.right_bottom() + vec2(-TEXT_RIGHT, -TEXT_BOTTOM) - desired_size;
@ -365,7 +365,9 @@ impl RelayEntry {
let response = ui
.interact(btn_rect, id, Sense::click())
.on_hover_cursor(egui::CursorIcon::PointingHand);
response.widget_info(|| WidgetInfo::labeled(WidgetType::Button, galley.text()));
response.widget_info(|| {
WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), galley.text())
});
let visuals = ui.style().interact(&response);
{

View File

@ -75,7 +75,12 @@ impl<'a> Switch<'a> {
fn allocate(&mut self, ui: &mut Ui) -> (Response, Option<Arc<Galley>>) {
let (extra_width, galley) = if let Some(text) = self.label.take() {
let available_width = ui.available_width() - self.size.y - ui.spacing().item_spacing.y;
let galley = text.into_galley(ui, Some(false), available_width, TextStyle::Body);
let galley = text.into_galley(
ui,
Some(egui::TextWrapMode::Truncate),
available_width,
TextStyle::Body,
);
(
galley.rect.width() + ui.spacing().item_spacing.y,
Some(galley),
@ -136,7 +141,9 @@ pub fn switch_custom_at(
} else {
response
};
response.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::Checkbox, *value, ""));
response.widget_info(|| {
egui::WidgetInfo::selected(egui::WidgetType::Checkbox, ui.is_enabled(), *value, "")
});
if ui.is_rect_visible(rect) {
let how_on = ui.ctx().animate_bool(response.id, *value);
@ -204,7 +211,12 @@ fn interact(
let text = label.unwrap_or("".into());
response.widget_info(|| {
egui::WidgetInfo::selected(egui::WidgetType::Checkbox, *value, text.text())
egui::WidgetInfo::selected(
egui::WidgetType::Checkbox,
ui.is_enabled(),
*value,
text.text(),
)
});
(state, response)

View File

@ -1,3 +1,4 @@
use eframe::egui::style::TextCursorStyle;
use egui_winit::egui::{
self, load::SizedTexture, vec2, Color32, Rect, Rounding, Sense, Stroke, TextBuffer,
TextureHandle, Widget, WidgetText,
@ -161,7 +162,7 @@ impl<'t> TextEdit<'t> {
{
let theme = self.theme;
let response = &output.response;
let frame_rect = response.rect;
let frame_rect = response.rect + margin;
// this is how egui chooses the visual style:
#[allow(clippy::if_same_then_else)]
@ -290,7 +291,10 @@ impl TextEdit<'_> {
let visuals = ui.visuals_mut();
// cursor (enabled)
visuals.text_cursor = Stroke::new(3.0, theme.accent_color());
visuals.text_cursor = TextCursorStyle {
stroke: Stroke::new(3.0, theme.accent_color()),
..Default::default()
};
if visuals.dark_mode {
// text color (enabled)

View File

@ -177,7 +177,10 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
ui.label("or");
ui.menu_button("▼ Pick from Top Relays", |ui| {
for (url, _relay) in relay_options.iter() {
if ui.add(Button::new(url.as_str()).wrap(false)).clicked() {
if ui
.add(Button::new(url.as_str()).wrap_mode(egui::TextWrapMode::Extend))
.clicked()
{
app.wizard_state.relay_url = url.as_str().to_owned();
}
}

View File

@ -1,7 +1,6 @@
use super::{GossipUi, Page};
use crate::ui::widgets::CopyButton;
use eframe::egui;
use egui::style::Margin;
use eframe::egui::{self, Margin};
use egui::{Color32, Context, Frame, Stroke, Ui};
use gossip_lib::comms::ToOverlordMessage;
use gossip_lib::{Globals, GLOBALS};