diff --git a/Cargo.lock b/Cargo.lock
index 7eb08776f..6d11f2ba0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1958,7 +1958,7 @@ dependencies = [
[[package]]
name = "iced"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"iced_core",
"iced_futures",
@@ -1972,7 +1972,7 @@ dependencies = [
[[package]]
name = "iced_core"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"bitflags 2.5.0",
"bytes",
@@ -1991,7 +1991,7 @@ dependencies = [
[[package]]
name = "iced_futures"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"futures",
"iced_core",
@@ -2005,7 +2005,7 @@ dependencies = [
[[package]]
name = "iced_graphics"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"bitflags 2.5.0",
"bytemuck",
@@ -2026,7 +2026,7 @@ dependencies = [
[[package]]
name = "iced_renderer"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"iced_graphics",
"iced_tiny_skia",
@@ -2038,7 +2038,7 @@ dependencies = [
[[package]]
name = "iced_runtime"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"bytes",
"iced_core",
@@ -2050,7 +2050,7 @@ dependencies = [
[[package]]
name = "iced_tiny_skia"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"bytemuck",
"cosmic-text",
@@ -2065,7 +2065,7 @@ dependencies = [
[[package]]
name = "iced_wgpu"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"bitflags 2.5.0",
"bytemuck",
@@ -2084,7 +2084,7 @@ dependencies = [
[[package]]
name = "iced_widget"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"iced_renderer",
"iced_runtime",
@@ -2099,7 +2099,7 @@ dependencies = [
[[package]]
name = "iced_winit"
version = "0.13.0-dev"
-source = "git+https://github.com/tarkah/iced?rev=1feb29ab4badb35a57ff5c612f25009e8e175766#1feb29ab4badb35a57ff5c612f25009e8e175766"
+source = "git+https://github.com/iced-rs/iced?rev=6734d183594ebf89b8e6c030ea69d53ecb6b72db#6734d183594ebf89b8e6c030ea69d53ecb6b72db"
dependencies = [
"iced_futures",
"iced_graphics",
diff --git a/Cargo.toml b/Cargo.toml
index dbb59fe75..210346bce 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -58,5 +58,5 @@ windows_exe_info = "0.4"
members = ["data", "ipc", "irc", "irc/proto"]
[patch.crates-io]
-iced = { git = "https://github.com/tarkah/iced", rev = "1feb29ab4badb35a57ff5c612f25009e8e175766" }
-iced_core = { git = "https://github.com/tarkah/iced", rev = "1feb29ab4badb35a57ff5c612f25009e8e175766" }
+iced = { git = "https://github.com/iced-rs/iced", rev = "6734d183594ebf89b8e6c030ea69d53ecb6b72db" }
+iced_core = { git = "https://github.com/iced-rs/iced", rev = "6734d183594ebf89b8e6c030ea69d53ecb6b72db" }
diff --git a/book/src/guides/text-formatting.md b/book/src/guides/text-formatting.md
index 21587d3e7..6601d302e 100644
--- a/book/src/guides/text-formatting.md
+++ b/book/src/guides/text-formatting.md
@@ -11,6 +11,8 @@ Below is a table with the supported text attributes.
| _Italics_ | `_italic text_` | `$iitalic text$i` |
| **Bold** | `__bold text__` | `$bbold text$b` |
| **_Italic and Bold_** | `___italic and bold___` | `$b$iitalic and bold$i$b` |
+| ~~Strikethrough~~ | `~~strikethrough~~` | `$sstrikethrough$s` |
+| Underline | - | `$uunderline$u` |
| Code | `` `code` `` | `$mcode$m` |
| Spoiler | `\|\|spoiler\|\|` | - |
@@ -20,8 +22,9 @@ Example
/format __this is bold__ $iand this is italic$i
```
-Will render the following:
-> __this is bold__ _and this is italic_
+Will render the following:
+
+> **this is bold** _and this is italic_
## Color
@@ -50,7 +53,7 @@ Colors
- 12 - lightblue
- 13 - pink
- 14 - grey
- - 15 - lightgrey
+ - 15 - lightgrey
Example
@@ -59,13 +62,12 @@ Example
/format $c04,09foobar$c
```
-Will both render the following:
+Will both render the following:
foobar
-
## Configuration
By default, Halloy will only format text when using the `/format` command. This, however, can be changed with the `auto_format` configuration option:
diff --git a/data/src/message/formatting/encode.rs b/data/src/message/formatting/encode.rs
index 32f0e245c..5449df8a1 100644
--- a/data/src/message/formatting/encode.rs
+++ b/data/src/message/formatting/encode.rs
@@ -213,6 +213,7 @@ fn markdown<'a>(
let italic = alt((relaxed_run('*', 1), strict_run('_', 1)));
let bold = alt((relaxed_run('*', 2), strict_run('_', 2)));
let italic_bold = alt((relaxed_run('*', 3), strict_run('_', 3)));
+ let strikethrough = relaxed_run('~', 2);
let spoiler = relaxed_run('|', 2);
let code = map(
alt((
@@ -232,6 +233,7 @@ fn markdown<'a>(
map(italic_bold, Markdown::ItalicBold),
map(bold, Markdown::Bold),
map(italic, Markdown::Italic),
+ map(strikethrough, Markdown::Strikethrough),
map(spoiler, Markdown::Spoiler),
map(code, Markdown::Code),
))
@@ -282,6 +284,8 @@ fn dollar(input: &str) -> IResult<&str, Dollar> {
map(tag("$b"), |_| Dollar::Bold),
map(tag("$i"), |_| Dollar::Italics),
map(tag("$m"), |_| Dollar::Monospace),
+ map(tag("$s"), |_| Dollar::Strikethrough),
+ map(tag("$u"), |_| Dollar::Underline),
map(tag("$r"), |_| Dollar::Reset),
map(start_color, |(fg, bg)| Dollar::StartColor(fg, bg)),
// No valid colors after code == end
@@ -346,6 +350,14 @@ impl Token {
}
out.push(c);
}
+ Markdown::Strikethrough(tokens) => {
+ let m = Modifier::Strikethrough.char();
+ out.push(m);
+ for token in tokens {
+ token.encode(out);
+ }
+ out.push(m);
+ }
},
Token::Dollar(dollar) => match dollar {
Dollar::Bold => {
@@ -357,6 +369,12 @@ impl Token {
Dollar::Monospace => {
out.push(Modifier::Monospace.char());
}
+ Dollar::Strikethrough => {
+ out.push(Modifier::Strikethrough.char());
+ }
+ Dollar::Underline => {
+ out.push(Modifier::Underline.char());
+ }
Dollar::Reset => {
out.push(Modifier::Reset.char());
}
@@ -383,6 +401,7 @@ enum Markdown {
Bold(Vec),
Italic(Vec),
ItalicBold(Vec),
+ Strikethrough(Vec),
Code(Vec),
Spoiler(Vec),
}
@@ -392,6 +411,8 @@ enum Dollar {
Bold,
Italics,
Monospace,
+ Strikethrough,
+ Underline,
Reset,
StartColor(Color, Option),
EndColor,
diff --git a/src/buffer/input_view/format_tooltip.txt b/src/buffer/input_view/format_tooltip.txt
index f778d9256..6218b4493 100644
--- a/src/buffer/input_view/format_tooltip.txt
+++ b/src/buffer/input_view/format_tooltip.txt
@@ -4,11 +4,14 @@ __bold__
___italic & bold___
`code`
||spoiler||
+~~strikethrough~~
Toggles:
$b - bold
$i - italic
$m - monospace
+$s - strikethrough
+$u - underline
$r - reset
Color:
diff --git a/src/widget.rs b/src/widget.rs
index 11b4ffa69..138e9a9e4 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -1,6 +1,5 @@
#![allow(dead_code)]
use data::message;
-use iced::advanced::text::Background;
use iced::border;
use iced::widget::span;
@@ -62,23 +61,22 @@ pub fn message_content<'a, M: 'a>(
.color_maybe(
formatting
.fg
- .and_then(|color| color.into_iced(theme.colors()))
- .or_else(|| {
- formatting.monospace.then_some(theme.colors().error.darker)
- }),
+ .and_then(|color| color.into_iced(theme.colors())),
)
.background_maybe(
formatting
.bg
- .and_then(|color| color.into_iced(theme.colors()))
- .map(Background::from)
- .or_else(|| {
- formatting.monospace.then_some(Background {
- color: theme.colors().background.lighter,
- border: border::rounded(3),
- })
- }),
- );
+ .and_then(|color| color.into_iced(theme.colors())),
+ )
+ .underline(formatting.underline)
+ .strikethrough(formatting.strikethrough);
+
+ if formatting.monospace {
+ span = span
+ .color(theme.colors().error.darker)
+ .background(theme.colors().background.lighter)
+ .border(border::rounded(3));
+ }
match (formatting.bold, formatting.italics) {
(true, true) => {
diff --git a/src/widget/selectable_rich_text.rs b/src/widget/selectable_rich_text.rs
index 9248e7990..44c4d4875 100644
--- a/src/widget/selectable_rich_text.rs
+++ b/src/widget/selectable_rich_text.rs
@@ -2,7 +2,7 @@ use iced::advanced::graphics::core::touch;
use iced::advanced::layout;
use iced::advanced::renderer;
use iced::advanced::renderer::Quad;
-use iced::advanced::text::Background;
+use iced::advanced::text::Highlight;
use iced::advanced::text::{self, Paragraph, Span, Text};
use iced::advanced::widget::tree::{self, Tree};
use iced::advanced::widget::Operation;
@@ -17,7 +17,8 @@ use iced::Border;
use iced::Length;
use iced::Point;
use iced::Shadow;
-use iced::{self, Color, Element, Event, Pixels, Rectangle, Size};
+use iced::Vector;
+use iced::{self, Background, Color, Element, Event, Pixels, Rectangle, Size};
use itertools::Itertools;
use super::selectable_text::{selection, Catalog, Interaction, Style, StyleFn};
@@ -194,7 +195,7 @@ struct State {
span_pressed: Option,
paragraph: P,
interaction: Interaction,
- shown_spoiler: Option<(usize, Color, Background)>,
+ shown_spoiler: Option<(usize, Color, Highlight)>,
}
impl<'a, Message, Link, Theme, Renderer> Widget
@@ -341,8 +342,8 @@ where
if state.shown_spoiler.is_none() {
// Find if spoiler is hovered
for (index, span) in state.spans.iter().enumerate() {
- if let Some((fg, bg)) = span.color.zip(span.background) {
- let is_spoiler = fg == bg.color;
+ if let Some((fg, highlight)) = span.color.zip(span.highlight) {
+ let is_spoiler = highlight.background == Background::Color(fg);
if is_spoiler
&& state
@@ -351,7 +352,7 @@ where
.into_iter()
.any(|bounds| bounds.contains(cursor))
{
- state.shown_spoiler = Some((index, fg, bg));
+ state.shown_spoiler = Some((index, fg, highlight));
break;
}
}
@@ -362,7 +363,7 @@ where
// Safe we just got this index
let span = &mut state.spans[index];
span.color = None;
- span.background = None;
+ span.highlight = None;
state.paragraph = Renderer::Paragraph::with_spans(text_with_spans(
state.spans.as_ref(),
));
@@ -370,10 +371,10 @@ where
}
}
// Hide spoiler
- else if let Some((index, fg, bg)) = state.shown_spoiler.take() {
+ else if let Some((index, fg, highlight)) = state.shown_spoiler.take() {
if let Some(span) = state.spans.get_mut(index) {
span.color = Some(fg);
- span.background = Some(bg);
+ span.highlight = Some(highlight);
}
state.paragraph =
Renderer::Paragraph::with_spans(text_with_spans(state.spans.as_ref()));
@@ -392,7 +393,7 @@ where
theme: &Theme,
defaults: &renderer::Style,
layout: Layout<'_>,
- _cursor_position: mouse::Cursor,
+ cursor: mouse::Cursor,
viewport: &Rectangle,
) {
let bounds = layout.bounds();
@@ -407,20 +408,88 @@ where
let style = theme.style(&self.class);
- // Draw backgrounds
+ let hovered_span = cursor
+ .position_in(layout.bounds())
+ .and_then(|position| state.paragraph.hit_span(position));
+
for (index, span) in state.spans.iter().enumerate() {
- if let Some(background) = span.background {
+ let is_hovered_link = span.link.is_some() && Some(index) == hovered_span;
+
+ if span.highlight.is_some() || span.underline || span.strikethrough || is_hovered_link {
let translation = layout.position() - Point::ORIGIN;
+ let regions = state.paragraph.span_bounds(index);
+
+ if let Some(highlight) = span.highlight {
+ for bounds in ®ions {
+ let bounds = Rectangle::new(
+ bounds.position() - Vector::new(span.padding.left, span.padding.top),
+ bounds.size()
+ + Size::new(span.padding.horizontal(), span.padding.vertical()),
+ );
+
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: bounds + translation,
+ border: highlight.border,
+ ..Default::default()
+ },
+ highlight.background,
+ );
+ }
+ }
- for bounds in state.paragraph.span_bounds(index) {
- renderer.fill_quad(
- renderer::Quad {
- bounds: bounds + translation,
- border: background.border,
- ..Default::default()
- },
- background.color,
- );
+ if span.underline || span.strikethrough || is_hovered_link {
+ let size = span.size.or(self.size).unwrap_or(renderer.default_size());
+
+ let line_height = span
+ .line_height
+ .unwrap_or(self.line_height)
+ .to_absolute(size);
+
+ let color = span.color.or(style.color).unwrap_or(defaults.text_color);
+
+ 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 {
+ for bounds in ®ions {
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: snapped(Rectangle::new(
+ bounds.position() + baseline
+ - Vector::new(0.0, size.0 * 0.08),
+ Size::new(bounds.width, 1.0),
+ )),
+ ..Default::default()
+ },
+ color,
+ );
+ }
+ }
+
+ if span.strikethrough {
+ for bounds in ®ions {
+ renderer.fill_quad(
+ renderer::Quad {
+ bounds: snapped(Rectangle::new(
+ bounds.position() + baseline
+ - Vector::new(0.0, size.0 / 2.0),
+ Size::new(bounds.width, 1.0),
+ )),
+ ..Default::default()
+ },
+ color,
+ );
+ }
+ }
}
}
}
@@ -582,7 +651,7 @@ where
if let Some((index, _, _)) = state.shown_spoiler {
if let Some(span) = state.spans.get_mut(index) {
span.color = None;
- span.background = None;
+ span.highlight = None;
}
}
@@ -610,7 +679,7 @@ where
if let Some((index, _, _)) = state.shown_spoiler {
if let Some(span) = state.spans.get_mut(index) {
span.color = None;
- span.background = None;
+ span.highlight = None;
}
}