Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions crates/component-zoo/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ live_design! {
use makepad_component::widgets::popover::*;
use makepad_component::widgets::label::*;
use makepad_component::widgets::text::*;
use makepad_component::widgets::link::*;
use makepad_component::widgets::alert::*;

// ============================================================
Expand Down Expand Up @@ -998,6 +999,68 @@ live_design! {

<MpDivider> {}

// ===== Link Section =====
<View> {
width: Fill, height: Fit,
flow: Down,
spacing: 16,

<SectionHeader> { text: "Link" }

// Basic links
<View> {
width: Fit, height: Fit,
flow: Down,
spacing: 8,

<SubsectionLabel> { text: "Basic Links" }

<View> {
width: Fit, height: Fit,
flow: Right,
spacing: 16,

<MpLink> {
text: "Visit Makepad"
href: "https://makepad.nl"
}
<MpLink> {
text: "GitHub Repository"
href: "https://github.com/makepad/makepad"
}
<MpLink> {
text: "Documentation"
href: "https://makepad.dev"
}
}
}

// Inline with text
<View> {
width: Fit, height: Fit,
flow: Down,
spacing: 8,

<SubsectionLabel> { text: "Inline with Text" }

<View> {
width: Fit, height: Fit,
flow: Right,
spacing: 4,
align: { y: 0.5 }

<MpLabel> { text: "Check out" }
<MpLink> {
text: "our website"
href: "https://makepad.nl"
}
<MpLabel> { text: "for more information." }
}
}
}

<MpDivider> {}

// ===== Badge Section =====
<View> {
width: Fill, height: Fit,
Expand Down
226 changes: 226 additions & 0 deletions crates/ui/src/widgets/link.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
use makepad_widgets::*;

live_design! {
use link::theme::*;
use link::shaders::*;
use link::widgets::*;

use crate::theme::colors::*;

pub MpLink = {{MpLink}} {
width: Fit,
height: Fit,

draw_text: {
text_style: <THEME_FONT_REGULAR> { font_size: 14.0 }
color: (PRIMARY)

instance hover: 0.0
instance pressed: 0.0

fn get_color(self) -> vec4 {
return mix(
mix(self.color, self.color * 0.8, self.hover),
self.color * 0.6,
self.pressed
);
}
}

draw_underline: {
instance color: (PRIMARY)
instance hover: 0.0
instance pressed: 0.0

fn pixel(self) -> vec4 {
let sdf = Sdf2d::viewport(self.pos * self.rect_size);
let y = self.rect_size.y - 1.0;
sdf.move_to(0.0, y);
sdf.line_to(self.rect_size.x, y);

let line_color = mix(
mix(self.color, self.color * 0.8, self.hover),
self.color * 0.6,
self.pressed
);

return sdf.stroke(line_color, 1.0);
}
}

cursor: Hand

animator: {
hover = {
default: off
off = {
from: { all: Forward { duration: 0.15 } }
apply: {
draw_text: { hover: 0.0 }
draw_underline: { hover: 0.0 }
}
}
on = {
from: { all: Forward { duration: 0.15 } }
apply: {
draw_text: { hover: 1.0 }
draw_underline: { hover: 1.0 }
}
}
}
pressed = {
default: off
off = {
from: { all: Forward { duration: 0.1 } }
apply: {
draw_text: { pressed: 0.0 }
draw_underline: { pressed: 0.0 }
}
}
on = {
from: { all: Forward { duration: 0.1 } }
apply: {
draw_text: { pressed: 1.0 }
draw_underline: { pressed: 1.0 }
}
}
}
}

text: ""
}
}

#[derive(Live, LiveHook, Widget)]
pub struct MpLink {
#[redraw]
#[live]
draw_text: DrawText,

#[live]
draw_underline: DrawQuad,

#[walk]
walk: Walk,

#[layout]
layout: Layout,

#[live]
text: ArcStringMut,

#[live]
href: ArcStringMut,

#[live(false)]
disabled: bool,

#[animator]
animator: Animator,

#[rust]
area: Area,
}

#[derive(Clone, Debug, DefaultNone)]
pub enum MpLinkAction {
Clicked,
None,
}

impl Widget for MpLink {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
let uid = self.widget_uid();

if self.animator_handle_event(cx, event).must_redraw() {
self.redraw(cx);
}

if self.disabled {
return;
}

match event.hits(cx, self.area) {
Hit::FingerHoverIn(_) => {
cx.set_cursor(MouseCursor::Hand);
self.animator_play(cx, ids!(hover.on));
}
Hit::FingerHoverOut(_) => {
cx.set_cursor(MouseCursor::Default);
self.animator_play(cx, ids!(hover.off));
}
Hit::FingerDown(_) => {
self.animator_play(cx, ids!(pressed.on));
}
Hit::FingerUp(fe) => {
self.animator_play(cx, ids!(pressed.off));
if fe.is_over {
self.activate_link(cx, uid, scope);
}
}
Hit::KeyDown(ke) => {
if ke.key_code == KeyCode::ReturnKey || ke.key_code == KeyCode::Space {
self.animator_play(cx, ids!(pressed.on));
self.activate_link(cx, uid, scope);
}
}
_ => {}
}
}

fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
self.draw_underline.begin(cx, walk, self.layout);
self.draw_text.draw_walk(cx, Walk::fit(), Align::default(), self.text.as_ref());
self.draw_underline.end(cx);
self.area = self.draw_underline.area();
DrawStep::done()
}
}

impl MpLink {
fn activate_link(&mut self, cx: &mut Cx, uid: WidgetUid, scope: &Scope) {
let href = self.href.as_ref();
if !href.is_empty() {
cx.open_url(href, OpenUrlInPlace::No);
}
cx.widget_action(uid, &scope.path, MpLinkAction::Clicked);
}

pub fn clicked(&self, actions: &Actions) -> bool {
if let Some(action) = actions.find_widget_action(self.widget_uid()) {
matches!(action.cast::<MpLinkAction>(), MpLinkAction::Clicked)
} else {
false
}
}

pub fn set_text(&mut self, text: &str) {
self.text.as_mut_empty().push_str(text);
}

pub fn set_href(&mut self, href: &str) {
self.href.as_mut_empty().push_str(href);
}
}

impl MpLinkRef {
pub fn clicked(&self, actions: &Actions) -> bool {
if let Some(inner) = self.borrow() {
inner.clicked(actions)
} else {
false
}
}

pub fn set_text(&self, text: &str) {
if let Some(mut inner) = self.borrow_mut() {
inner.set_text(text);
}
}

pub fn set_href(&self, href: &str) {
if let Some(mut inner) = self.borrow_mut() {
inner.set_href(href);
}
}
}
3 changes: 3 additions & 0 deletions crates/ui/src/widgets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod divider;
pub mod dropdown;
pub mod input;
pub mod label;
pub mod link;
pub mod list;
pub mod modal;
pub mod notification;
Expand All @@ -32,6 +33,7 @@ pub use checkbox::*;
pub use divider::*;
pub use input::*;
pub use label::*;
pub use link::*;
pub use page_flip::*;
pub use progress::*;
pub use radio::*;
Expand Down Expand Up @@ -63,6 +65,7 @@ pub fn live_design(cx: &mut Cx) {
crate::widgets::dropdown::live_design(cx);
crate::widgets::input::live_design(cx);
crate::widgets::label::live_design(cx);
crate::widgets::link::live_design(cx);
crate::widgets::list::live_design(cx);
crate::widgets::modal::live_design(cx);
crate::widgets::notification::live_design(cx);
Expand Down