Skip to content

Commit

Permalink
Merge pull request #563 from squidowl/feat/clickable-user-fragment
Browse files Browse the repository at this point in the history
Make user fragment clickable
  • Loading branch information
tarkah authored Sep 14, 2024
2 parents a18cd97 + 520f7f5 commit 66874c3
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 32 deletions.
6 changes: 6 additions & 0 deletions data/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,12 @@ pub fn reference_user_text(sender: NickRef, own_nick: NickRef, text: &str) -> bo
sender != own_nick && text.contains(own_nick.as_ref())
}

#[derive(Debug, Clone)]
pub enum Link {
Url(String),
User(User),
}

fn fail_as_none<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where
T: Deserialize<'de>,
Expand Down
16 changes: 12 additions & 4 deletions src/buffer/scroll_view.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use data::message::Limit;
use data::message::{self, Limit};
use data::server::Server;
use data::user::Nick;
use data::{history, time, Config};
Expand All @@ -19,7 +19,7 @@ pub enum Message {
viewport: scrollable::Viewport,
},
UserContext(user_context::Message),
Link(String),
Link(message::Link),
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -227,8 +227,16 @@ impl State {
user_context::update(message).map(Event::UserContext),
);
}
Message::Link(link) => {
let _ = open::that_detached(link);
Message::Link(message::Link::Url(url)) => {
let _ = open::that_detached(url);
}
Message::Link(message::Link::User(user)) => {
return (
Task::none(),
Some(Event::UserContext(user_context::Event::SingleClick(
user.nickname().to_owned(),
))),
)
}
}

Expand Down
11 changes: 7 additions & 4 deletions src/buffer/user_context.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use data::user::Nick;
use data::{Buffer, User};
use data::{message, Buffer, User};
use iced::widget::{button, container, horizontal_rule, row, text, Space};
use iced::{padding, Length, Padding};

Expand Down Expand Up @@ -53,7 +53,7 @@ pub enum Message {
SingleClick(Nick),
ToggleAccessLevel(Nick, String),
SendFile(Nick),
Link(String),
Link(message::Link),
}

#[derive(Debug, Clone)]
Expand All @@ -72,10 +72,13 @@ pub fn update(message: Message) -> Option<Event> {
Message::SingleClick(nick) => Some(Event::SingleClick(nick)),
Message::ToggleAccessLevel(nick, mode) => Some(Event::ToggleAccessLevel(nick, mode)),
Message::SendFile(nick) => Some(Event::SendFile(nick)),
Message::Link(link) => {
let _ = open::that_detached(link);
Message::Link(message::Link::Url(url)) => {
let _ = open::that_detached(url);
None
}
Message::Link(message::Link::User(user)) => {
Some(Event::SingleClick(user.nickname().to_owned()))
}
}
}

Expand Down
14 changes: 13 additions & 1 deletion src/theme/selectable_text.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use data::{message, user::NickColor};

use crate::widget::selectable_text::{Catalog, Style, StyleFn};
use crate::widget::{
selectable_rich_text,
selectable_text::{Catalog, Style, StyleFn},
};

use super::{text, Theme};

Expand Down Expand Up @@ -99,3 +102,12 @@ pub fn status(theme: &Theme, status: message::source::Status) -> Style {
selection_color: theme.colors().buffer.selection,
}
}

impl selectable_rich_text::Link for message::Link {
fn underline(&self) -> bool {
match self {
data::message::Link::Url(_) => true,
data::message::Link::User(_) => false,
}
}
}
8 changes: 5 additions & 3 deletions src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub type Button<'a, Message> = iced::widget::Button<'a, Message, Theme>;
pub fn message_content<'a, M: 'a>(
content: &'a message::Content,
theme: &'a Theme,
on_link: impl Fn(String) -> M + 'a,
on_link: impl Fn(message::Link) -> M + 'a,
style: impl Fn(&Theme) -> selectable_text::Style + 'a,
config: &Config,
) -> Element<'a, M> {
Expand All @@ -71,11 +71,13 @@ pub fn message_content<'a, M: 'a>(
None => theme.colors().text.primary,
};

span(user.nickname().to_string()).color(color)
span(user.nickname().to_string())
.color(color)
.link(message::Link::User(user.clone()))
}
data::message::Fragment::Url(s) => span(s.as_str())
.color(theme.colors().buffer.url)
.link(s.as_str().to_string()),
.link(message::Link::Url(s.as_str().to_string())),
data::message::Fragment::Formatted { text, formatting } => {
let mut span = span(text)
.color_maybe(
Expand Down
42 changes: 22 additions & 20 deletions src/widget/selectable_rich_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn selectable_rich_text<'a, Message, Link, Theme, Renderer>(
spans: impl Into<Cow<'a, [Span<'a, Link, Renderer::Font>]>>,
) -> Rich<'a, Message, Link, Theme, Renderer>
where
Link: Clone + 'static,
Link: self::Link + 'static,
Theme: Catalog,
Renderer: text::Renderer,
{
Expand All @@ -41,7 +41,7 @@ where
#[allow(missing_debug_implementations)]
pub struct Rich<'a, Message, Link = (), Theme = iced::Theme, Renderer = iced::Renderer>
where
Link: Clone + 'static,
Link: self::Link + 'static,
Theme: Catalog,
Renderer: text::Renderer,
{
Expand All @@ -59,7 +59,7 @@ where

impl<'a, Message, Link, Theme, Renderer> Rich<'a, Message, Link, Theme, Renderer>
where
Link: Clone + 'static,
Link: self::Link + 'static,
Theme: Catalog,
Renderer: text::Renderer,
{
Expand Down Expand Up @@ -181,7 +181,7 @@ where

impl<'a, Message, Link, Theme, Renderer> Default for Rich<'a, Message, Link, Theme, Renderer>
where
Link: Clone + 'static,
Link: self::Link + 'static,
Theme: Catalog,
Renderer: text::Renderer,
{
Expand All @@ -190,6 +190,14 @@ where
}
}

pub trait Link: Clone {
fn underline(&self) -> bool {
true
}
}

impl Link for () {}

struct State<Link, P: Paragraph> {
spans: Vec<Span<'static, Link, P::Font>>,
span_pressed: Option<usize>,
Expand All @@ -201,7 +209,7 @@ struct State<Link, P: Paragraph> {
impl<'a, Message, Link, Theme, Renderer> Widget<Message, Theme, Renderer>
for Rich<'a, Message, Link, Theme, Renderer>
where
Link: Clone + 'static,
Link: self::Link + 'static,
Theme: Catalog,
Renderer: text::Renderer,
{
Expand Down Expand Up @@ -451,23 +459,17 @@ where
let baseline =
translation + Vector::new(0.0, size.0 + (line_height.0 - size.0) / 2.0);

let snapped = |rect: Rectangle| {
let fract = viewport.y.fract();
let offset = if fract >= 0.5 { -(1.0 - fract) } else { fract };
let adj = (rect.y - offset).round() + offset;

Rectangle { y: adj, ..rect }
};

if span.underline || is_hovered_link {
if span.underline
|| (is_hovered_link && span.link.as_ref().unwrap().underline())
{
for bounds in &regions {
renderer.fill_quad(
renderer::Quad {
bounds: snapped(Rectangle::new(
bounds: Rectangle::new(
bounds.position() + baseline
- Vector::new(0.0, size.0 * 0.08),
Size::new(bounds.width, 1.0),
)),
),
..Default::default()
},
color,
Expand All @@ -479,11 +481,11 @@ where
for bounds in &regions {
renderer.fill_quad(
renderer::Quad {
bounds: snapped(Rectangle::new(
bounds: Rectangle::new(
bounds.position() + baseline
- Vector::new(0.0, size.0 / 2.0),
Size::new(bounds.width, 1.0),
)),
),
..Default::default()
},
color,
Expand Down Expand Up @@ -696,7 +698,7 @@ where
impl<'a, Message, Link, Theme, Renderer> FromIterator<Span<'a, Link, Renderer::Font>>
for Rich<'a, Message, Link, Theme, Renderer>
where
Link: Clone + 'static,
Link: self::Link + 'static,
Theme: Catalog,
Renderer: text::Renderer,
{
Expand All @@ -712,7 +714,7 @@ impl<'a, Message, Link, Theme, Renderer> From<Rich<'a, Message, Link, Theme, Ren
for Element<'a, Message, Theme, Renderer>
where
Message: 'a,
Link: Clone + 'static,
Link: self::Link + 'static,
Theme: Catalog + 'a,
Renderer: text::Renderer + 'a,
{
Expand Down

0 comments on commit 66874c3

Please sign in to comment.