Skip to content

Commit

Permalink
Add Link widget (#1506)
Browse files Browse the repository at this point in the history
This looks like a Hyperlink, but doesn't do anything when clicked.
Or rather: it lets the user decide what happens on click.

Closes #1152
  • Loading branch information
emilk authored Apr 16, 2022
1 parent 96335d5 commit 2d2022f
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 33 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `Frame::outer_margin`.
* Added `Painter::hline` and `Painter::vline`.
* Added `Link` and `ui.link` ([#1506](https://github.com/emilk/egui/pull/1506)).

### Changed 🔧
* `ClippedMesh` has been replaced with `ClippedPrimitive` ([#1351](https://github.com/emilk/egui/pull/1351)).
Expand All @@ -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)).

### 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)).
* 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)).
Expand Down
2 changes: 1 addition & 1 deletion egui/src/data/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ impl WidgetInfo {

// TODO: localization
let widget_type = match typ {
WidgetType::Hyperlink => "link",
WidgetType::Link => "link",
WidgetType::TextEdit => "text edit",
WidgetType::Button => "button",
WidgetType::Checkbox => "checkbox",
Expand Down
3 changes: 2 additions & 1 deletion egui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,8 @@ pub mod special_emojis {
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum WidgetType {
Label, // TODO: emit Label events
Hyperlink,
/// e.g. a hyperlink
Link,
TextEdit,
Button,
Checkbox,
Expand Down
30 changes: 28 additions & 2 deletions egui/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1227,14 +1227,40 @@ impl Ui {
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`].
pub fn hyperlink(&mut self, url: impl ToString) -> Response {
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| {
Expand Down
94 changes: 66 additions & 28 deletions egui/src/widgets/hyperlink.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,69 @@
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"`.
///
/// See also [`Ui::hyperlink`] and [`Ui::hyperlink_to`].
Expand Down Expand Up @@ -42,15 +106,9 @@ impl Hyperlink {

impl Widget for Hyperlink {
fn ui(self, ui: &mut Ui) -> Response {
let Hyperlink { url, text } = self;
let label = Label::new(text).sense(Sense::click());
let Self { url, text } = self;

let (pos, text_galley, response) = label.layout_in_ui(ui);
response.widget_info(|| WidgetInfo::labeled(WidgetType::Hyperlink, text_galley.text()));

if response.hovered() {
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
}
let response = ui.add(Link::new(text));
if response.clicked() {
let modifiers = ui.ctx().input().modifiers;
ui.ctx().output().open_url = Some(crate::output::OpenUrl {
Expand All @@ -64,26 +122,6 @@ impl Widget for Hyperlink {
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)
}
}
6 changes: 6 additions & 0 deletions egui_demo_lib/src/apps/demo/widget_gallery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ impl WidgetGallery {
}
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.checkbox(boolean, "Checkbox");
ui.end_row();
Expand Down

0 comments on commit 2d2022f

Please sign in to comment.