Add Link widget (#1506)
This looks like a Hyperlink, but doesn't do anything when clicked. Or rather: it lets the user decide what happens on click. Closes https://github.com/emilk/egui/issues/1152
This commit is contained in:
parent
96335d5f45
commit
2d2022fb72
6 changed files with 105 additions and 33 deletions
|
@ -14,6 +14,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
|
||||||
* Added `Ui::push_id` to resolve id clashes ([#1374](https://github.com/emilk/egui/pull/1374)).
|
* Added `Ui::push_id` to resolve id clashes ([#1374](https://github.com/emilk/egui/pull/1374)).
|
||||||
* Added `Frame::outer_margin`.
|
* Added `Frame::outer_margin`.
|
||||||
* Added `Painter::hline` and `Painter::vline`.
|
* Added `Painter::hline` and `Painter::vline`.
|
||||||
|
* Added `Link` and `ui.link` ([#1506](https://github.com/emilk/egui/pull/1506)).
|
||||||
|
|
||||||
### Changed 🔧
|
### Changed 🔧
|
||||||
* `ClippedMesh` has been replaced with `ClippedPrimitive` ([#1351](https://github.com/emilk/egui/pull/1351)).
|
* `ClippedMesh` has been replaced with `ClippedPrimitive` ([#1351](https://github.com/emilk/egui/pull/1351)).
|
||||||
|
@ -27,7 +28,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
|
||||||
* Renamed the feature `serialize` to `serde` ([#1467](https://github.com/emilk/egui/pull/1467)).
|
* Renamed the feature `serialize` to `serde` ([#1467](https://github.com/emilk/egui/pull/1467)).
|
||||||
|
|
||||||
### Fixed 🐛
|
### Fixed 🐛
|
||||||
* Fixed ComboBoxes always being rendered left-aligned ([#1304](https://github.com/emilk/egui/pull/1304)).
|
* Fixed `ComboBox`:es always being rendered left-aligned ([#1304](https://github.com/emilk/egui/pull/1304)).
|
||||||
* Fixed ui code that could lead to a deadlock ([#1380](https://github.com/emilk/egui/pull/1380)).
|
* Fixed ui code that could lead to a deadlock ([#1380](https://github.com/emilk/egui/pull/1380)).
|
||||||
* Text is darker and more readable in bright mode ([#1412](https://github.com/emilk/egui/pull/1412)).
|
* Text is darker and more readable in bright mode ([#1412](https://github.com/emilk/egui/pull/1412)).
|
||||||
* Fixed `Ui::add_visible` sometimes leaving the `Ui` in a disabled state. ([#1436](https://github.com/emilk/egui/issues/1436)).
|
* Fixed `Ui::add_visible` sometimes leaving the `Ui` in a disabled state. ([#1436](https://github.com/emilk/egui/issues/1436)).
|
||||||
|
|
|
@ -466,7 +466,7 @@ impl WidgetInfo {
|
||||||
|
|
||||||
// TODO: localization
|
// TODO: localization
|
||||||
let widget_type = match typ {
|
let widget_type = match typ {
|
||||||
WidgetType::Hyperlink => "link",
|
WidgetType::Link => "link",
|
||||||
WidgetType::TextEdit => "text edit",
|
WidgetType::TextEdit => "text edit",
|
||||||
WidgetType::Button => "button",
|
WidgetType::Button => "button",
|
||||||
WidgetType::Checkbox => "checkbox",
|
WidgetType::Checkbox => "checkbox",
|
||||||
|
|
|
@ -494,7 +494,8 @@ pub mod special_emojis {
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub enum WidgetType {
|
pub enum WidgetType {
|
||||||
Label, // TODO: emit Label events
|
Label, // TODO: emit Label events
|
||||||
Hyperlink,
|
/// e.g. a hyperlink
|
||||||
|
Link,
|
||||||
TextEdit,
|
TextEdit,
|
||||||
Button,
|
Button,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
|
|
|
@ -1227,14 +1227,40 @@ impl Ui {
|
||||||
Label::new(text.into().weak()).ui(self)
|
Label::new(text.into().weak()).ui(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shortcut for `add(Hyperlink::new(url))`
|
/// Looks like a hyperlink.
|
||||||
|
///
|
||||||
|
/// Shortcut for `add(Link::new(text))`.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # egui::__run_test_ui(|ui| {
|
||||||
|
/// if ui.link("Documentation").clicked() {
|
||||||
|
/// // …
|
||||||
|
/// }
|
||||||
|
/// # });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// See also [`Link`].
|
||||||
|
#[must_use = "You should check if the user clicked this with `if ui.link(…).clicked() { … } "]
|
||||||
|
pub fn link(&mut self, text: impl Into<WidgetText>) -> Response {
|
||||||
|
Link::new(text).ui(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Link to a web page.
|
||||||
|
///
|
||||||
|
/// Shortcut for `add(Hyperlink::new(url))`.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # egui::__run_test_ui(|ui| {
|
||||||
|
/// ui.hyperlink("https://www.egui.rs/");
|
||||||
|
/// # });
|
||||||
|
/// ```
|
||||||
///
|
///
|
||||||
/// See also [`Hyperlink`].
|
/// See also [`Hyperlink`].
|
||||||
pub fn hyperlink(&mut self, url: impl ToString) -> Response {
|
pub fn hyperlink(&mut self, url: impl ToString) -> Response {
|
||||||
Hyperlink::new(url).ui(self)
|
Hyperlink::new(url).ui(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shortcut for `add(Hyperlink::new(url).text(label))`
|
/// Shortcut for `add(Hyperlink::new(url).text(label))`.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # egui::__run_test_ui(|ui| {
|
/// # egui::__run_test_ui(|ui| {
|
||||||
|
|
|
@ -1,5 +1,69 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
/// Clickable text, that looks like a hyperlink.
|
||||||
|
///
|
||||||
|
/// To link to a web page, use [`Hyperlink`], [`Ui::hyperlink`] or [`Ui::hyperlink_to`].
|
||||||
|
///
|
||||||
|
/// See also [`Ui::link`].
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # egui::__run_test_ui(|ui| {
|
||||||
|
/// // These are equivalent:
|
||||||
|
/// if ui.link("Documentation").clicked() {
|
||||||
|
/// // …
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// if ui.add(egui::Link::new("Documentation")).clicked() {
|
||||||
|
/// // …
|
||||||
|
/// }
|
||||||
|
/// # });
|
||||||
|
/// ```
|
||||||
|
#[must_use = "You should put this widget in an ui with `ui.add(widget);`"]
|
||||||
|
pub struct Link {
|
||||||
|
text: WidgetText,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Link {
|
||||||
|
pub fn new(text: impl Into<WidgetText>) -> Self {
|
||||||
|
Self { text: text.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Link {
|
||||||
|
fn ui(self, ui: &mut Ui) -> Response {
|
||||||
|
let Link { text } = self;
|
||||||
|
let label = Label::new(text).sense(Sense::click());
|
||||||
|
|
||||||
|
let (pos, text_galley, response) = label.layout_in_ui(ui);
|
||||||
|
response.widget_info(|| WidgetInfo::labeled(WidgetType::Link, text_galley.text()));
|
||||||
|
|
||||||
|
if response.hovered() {
|
||||||
|
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.is_rect_visible(response.rect) {
|
||||||
|
let color = ui.visuals().hyperlink_color;
|
||||||
|
let visuals = ui.style().interact(&response);
|
||||||
|
|
||||||
|
let underline = if response.hovered() || response.has_focus() {
|
||||||
|
Stroke::new(visuals.fg_stroke.width, color)
|
||||||
|
} else {
|
||||||
|
Stroke::none()
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.painter().add(epaint::TextShape {
|
||||||
|
pos,
|
||||||
|
galley: text_galley.galley,
|
||||||
|
override_text_color: Some(color),
|
||||||
|
underline,
|
||||||
|
angle: 0.0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A clickable hyperlink, e.g. to `"https://github.com/emilk/egui"`.
|
/// A clickable hyperlink, e.g. to `"https://github.com/emilk/egui"`.
|
||||||
///
|
///
|
||||||
/// See also [`Ui::hyperlink`] and [`Ui::hyperlink_to`].
|
/// See also [`Ui::hyperlink`] and [`Ui::hyperlink_to`].
|
||||||
|
@ -42,15 +106,9 @@ impl Hyperlink {
|
||||||
|
|
||||||
impl Widget for Hyperlink {
|
impl Widget for Hyperlink {
|
||||||
fn ui(self, ui: &mut Ui) -> Response {
|
fn ui(self, ui: &mut Ui) -> Response {
|
||||||
let Hyperlink { url, text } = self;
|
let Self { url, text } = self;
|
||||||
let label = Label::new(text).sense(Sense::click());
|
|
||||||
|
|
||||||
let (pos, text_galley, response) = label.layout_in_ui(ui);
|
let response = ui.add(Link::new(text));
|
||||||
response.widget_info(|| WidgetInfo::labeled(WidgetType::Hyperlink, text_galley.text()));
|
|
||||||
|
|
||||||
if response.hovered() {
|
|
||||||
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
|
|
||||||
}
|
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
let modifiers = ui.ctx().input().modifiers;
|
let modifiers = ui.ctx().input().modifiers;
|
||||||
ui.ctx().output().open_url = Some(crate::output::OpenUrl {
|
ui.ctx().output().open_url = Some(crate::output::OpenUrl {
|
||||||
|
@ -64,26 +122,6 @@ impl Widget for Hyperlink {
|
||||||
new_tab: true,
|
new_tab: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui.is_rect_visible(response.rect) {
|
|
||||||
let color = ui.visuals().hyperlink_color;
|
|
||||||
let visuals = ui.style().interact(&response);
|
|
||||||
|
|
||||||
let underline = if response.hovered() || response.has_focus() {
|
|
||||||
Stroke::new(visuals.fg_stroke.width, color)
|
|
||||||
} else {
|
|
||||||
Stroke::none()
|
|
||||||
};
|
|
||||||
|
|
||||||
ui.painter().add(epaint::TextShape {
|
|
||||||
pos,
|
|
||||||
galley: text_galley.galley,
|
|
||||||
override_text_color: Some(color),
|
|
||||||
underline,
|
|
||||||
angle: 0.0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
response.on_hover_text(url)
|
response.on_hover_text(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,12 @@ impl WidgetGallery {
|
||||||
}
|
}
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
|
ui.add(doc_link_label("Link", "link"));
|
||||||
|
if ui.link("Click me!").clicked() {
|
||||||
|
*boolean = !*boolean;
|
||||||
|
}
|
||||||
|
ui.end_row();
|
||||||
|
|
||||||
ui.add(doc_link_label("Checkbox", "checkbox"));
|
ui.add(doc_link_label("Checkbox", "checkbox"));
|
||||||
ui.checkbox(boolean, "Checkbox");
|
ui.checkbox(boolean, "Checkbox");
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
Loading…
Reference in a new issue