From b07df2b43e283e3bb3fafe0908b319a6d0f7ccc4 Mon Sep 17 00:00:00 2001 From: TicClick Date: Sat, 17 Feb 2024 16:50:47 +0100 Subject: [PATCH] add clickable channel links --- crates/steel_core/src/chat/links.rs | 81 +++++++++++++++++------------ src/gui/chat.rs | 81 +++++++++++++++++------------ 2 files changed, 97 insertions(+), 65 deletions(-) diff --git a/crates/steel_core/src/chat/links.rs b/crates/steel_core/src/chat/links.rs index 8c5a85a..7ca01a6 100644 --- a/crates/steel_core/src/chat/links.rs +++ b/crates/steel_core/src/chat/links.rs @@ -4,6 +4,7 @@ use crate::chat::Message; pub enum LinkType { HTTP, HTTPS, + Channel, OSU(Action), } @@ -179,7 +180,7 @@ pub enum MessageChunk { Link { title: String, location: String, - protocol: LinkType, + link_type: LinkType, }, } @@ -201,7 +202,8 @@ impl Message { while i < bs.len() { // Only consider [[...]], [...], http(s)://, or osu(mp)://. // Yeah, I know there are other protocols and formats, but no. - while i < bs.len() && (bs[i] != b'[' && bs[i] != b'h' && bs[i] != b'o') { + while i < bs.len() && (bs[i] != b'[' && bs[i] != b'h' && bs[i] != b'o' && bs[i] != b'#') + { i += 1; } if i == bs.len() { @@ -224,6 +226,19 @@ impl Message { continue; } + // Channel name. + if i < bs.len() && bs[i] == b'#' { + i += 1; + while i < bs.len() && b'a' <= bs[i] && bs[i] <= b'z' { + i += 1; + } + links.push(LinkLocation::Raw { + pos: (start, i), + protocol: LinkType::Channel, + }); + continue; + } + // Wiki link. if i + 1 < bs.len() && bs[i + 1] == b'[' { while i < bs.len() && bs[i] != b']' { @@ -306,7 +321,7 @@ impl Message { ret.push(MessageChunk::Link { title: links[i].title(&self.text), location: links[i].location(&self.text), - protocol: links[i].protocol(), + link_type: links[i].protocol(), }); if i + 1 < links.len() { let next_pos = links[i + 1].position(); @@ -361,7 +376,7 @@ mod tests { vec![MessageChunk::Link { location: "http://test".into(), title: "Test (links here)]".into(), - protocol: LinkType::HTTP, + link_type: LinkType::HTTP, }] ); @@ -373,7 +388,7 @@ mod tests { MessageChunk::Link { location: "http://test".into(), title: "http://test".into(), - protocol: LinkType::HTTP, + link_type: LinkType::HTTP, }, MessageChunk::Text(" Test (links here)".into()), ] @@ -389,13 +404,13 @@ mod tests { MessageChunk::Link { location: "http://test".into(), title: "Test (links here)".into(), - protocol: LinkType::HTTP, + link_type: LinkType::HTTP, }, MessageChunk::Text(" ".into()), MessageChunk::Link { location: "http://test".into(), title: "Test (links here)".into(), - protocol: LinkType::HTTP, + link_type: LinkType::HTTP, } ] ); @@ -407,12 +422,12 @@ mod tests { MessageChunk::Link { location: "http://test".into(), title: "Test (links here)".into(), - protocol: LinkType::HTTP, + link_type: LinkType::HTTP, }, MessageChunk::Link { location: "http://test".into(), title: "Test (links here)".into(), - protocol: LinkType::HTTP, + link_type: LinkType::HTTP, }, MessageChunk::Text(" and after".into()), ] @@ -428,7 +443,7 @@ mod tests { MessageChunk::Link { location: "https://osu.ppy.sh/wiki/rules".into(), title: "wiki:rules".into(), - protocol: LinkType::HTTPS, + link_type: LinkType::HTTPS, }, MessageChunk::Text(" is the way to go".into()), ] @@ -441,7 +456,7 @@ mod tests { MessageChunk::Link { location: "https://osu.ppy.sh/wiki/rule".into(), title: "wiki:rule".into(), - protocol: LinkType::HTTPS, + link_type: LinkType::HTTPS, }, MessageChunk::Text("s]] is the way to go".into()), ] @@ -457,13 +472,13 @@ mod tests { MessageChunk::Link { location: "https://a".into(), title: "https://a".into(), - protocol: LinkType::HTTPS, + link_type: LinkType::HTTPS, }, MessageChunk::Text(" ".into()), MessageChunk::Link { location: "https://bhttps://".into(), title: "https://bhttps://".into(), - protocol: LinkType::HTTPS, + link_type: LinkType::HTTPS, }, MessageChunk::Text(" c".into()), ] @@ -479,18 +494,18 @@ mod tests { MessageChunk::Link { location: "https://ya.ru".into(), title: "https://ya.ru".into(), - protocol: LinkType::HTTPS, + link_type: LinkType::HTTPS, }, MessageChunk::Text(" ".into()), MessageChunk::Link { location: "http://example.com".into(), title: "example".into(), - protocol: LinkType::HTTP, + link_type: LinkType::HTTP, }, MessageChunk::Link { location: "https://osu.ppy.sh/wiki/silence".into(), title: "wiki:silence".into(), - protocol: LinkType::HTTPS, + link_type: LinkType::HTTPS, }, ] ); @@ -504,7 +519,7 @@ mod tests { vec![MessageChunk::Link { title: "osump://12345".into(), location: "osump://12345".into(), - protocol: LinkType::OSU(Action::Multiplayer(12345)), + link_type: LinkType::OSU(Action::Multiplayer(12345)), },] ); @@ -514,7 +529,7 @@ mod tests { vec![MessageChunk::Link { title: "osump://12345/".into(), location: "osump://12345/".into(), - protocol: LinkType::OSU(Action::Multiplayer(12345)), + link_type: LinkType::OSU(Action::Multiplayer(12345)), },] ); } @@ -527,7 +542,7 @@ mod tests { vec![MessageChunk::Link { title: "osu://dl/42311".into(), location: "osu://dl/42311".into(), - protocol: LinkType::OSU(Action::OpenBeatmap(42311)), + link_type: LinkType::OSU(Action::OpenBeatmap(42311)), },] ); @@ -537,7 +552,7 @@ mod tests { vec![MessageChunk::Link { title: "osu://dl/42311/".into(), location: "osu://dl/42311/".into(), - protocol: LinkType::OSU(Action::OpenBeatmap(42311)), + link_type: LinkType::OSU(Action::OpenBeatmap(42311)), },] ); @@ -547,7 +562,7 @@ mod tests { vec![MessageChunk::Link { title: "osu://dl/s/42311".into(), location: "osu://dl/s/42311".into(), - protocol: LinkType::OSU(Action::OpenBeatmap(42311)), + link_type: LinkType::OSU(Action::OpenBeatmap(42311)), },] ); @@ -557,7 +572,7 @@ mod tests { vec![MessageChunk::Link { title: "osu://dl/s/42311/".into(), location: "osu://dl/s/42311/".into(), - protocol: LinkType::OSU(Action::OpenBeatmap(42311)), + link_type: LinkType::OSU(Action::OpenBeatmap(42311)), },] ); } @@ -570,7 +585,7 @@ mod tests { vec![MessageChunk::Link { title: "osu://dl/b/641387".into(), location: "osu://dl/b/641387".into(), - protocol: LinkType::OSU(Action::OpenDifficulty(641387)), + link_type: LinkType::OSU(Action::OpenDifficulty(641387)), },] ); @@ -580,7 +595,7 @@ mod tests { vec![MessageChunk::Link { title: "osu://dl/b/641387/".into(), location: "osu://dl/b/641387/".into(), - protocol: LinkType::OSU(Action::OpenDifficulty(641387)), + link_type: LinkType::OSU(Action::OpenDifficulty(641387)), },] ); @@ -590,7 +605,7 @@ mod tests { vec![MessageChunk::Link { title: "osu://b/641387".into(), location: "osu://b/641387".into(), - protocol: LinkType::OSU(Action::OpenDifficulty(641387)), + link_type: LinkType::OSU(Action::OpenDifficulty(641387)), },] ); @@ -600,7 +615,7 @@ mod tests { vec![MessageChunk::Link { title: "osu://b/641387/".into(), location: "osu://b/641387/".into(), - protocol: LinkType::OSU(Action::OpenDifficulty(641387)), + link_type: LinkType::OSU(Action::OpenDifficulty(641387)), },] ); } @@ -614,13 +629,13 @@ mod tests { MessageChunk::Link { title: "osump://12345/".into(), location: "osump://12345/".into(), - protocol: LinkType::OSU(Action::Multiplayer(12345)), + link_type: LinkType::OSU(Action::Multiplayer(12345)), }, MessageChunk::Text(" ".into()), MessageChunk::Link { title: "osu://chan/#russian".into(), location: "osu://chan/#russian".into(), - protocol: LinkType::OSU(Action::Chat("#russian".into())), + link_type: LinkType::OSU(Action::Chat("#russian".into())), } ] ); @@ -635,13 +650,13 @@ mod tests { MessageChunk::Link { location: "osump://12345/".into(), title: "join my room".into(), - protocol: LinkType::OSU(Action::Multiplayer(12345)), + link_type: LinkType::OSU(Action::Multiplayer(12345)), }, MessageChunk::Text(" ".into()), MessageChunk::Link { location: "osu://chan/#osu".into(), title: "#chaos".into(), - protocol: LinkType::OSU(Action::Chat("#osu".into())), + link_type: LinkType::OSU(Action::Chat("#osu".into())), } ] ); @@ -657,19 +672,19 @@ mod tests { MessageChunk::Link { location: "osump://12345/".into(), title: "моя комната".into(), - protocol: LinkType::OSU(Action::Multiplayer(12345)), + link_type: LinkType::OSU(Action::Multiplayer(12345)), }, MessageChunk::Text(" ".into()), MessageChunk::Link { location: "osu://chan/#osu".into(), title: "#господичтоэто".into(), - protocol: LinkType::OSU(Action::Chat("#osu".into())), + link_type: LinkType::OSU(Action::Chat("#osu".into())), }, MessageChunk::Text(" ".into()), MessageChunk::Link { location: "osu://dl/123".into(), title: "非".into(), - protocol: LinkType::OSU(Action::OpenBeatmap(123)), + link_type: LinkType::OSU(Action::OpenBeatmap(123)), }, ] ); diff --git a/src/gui/chat.rs b/src/gui/chat.rs index 3412cda..3a68e09 100644 --- a/src/gui/chat.rs +++ b/src/gui/chat.rs @@ -554,33 +554,41 @@ fn format_chat_message_text( if let Some(chunks) = &msg.chunks { for c in chunks { match &c { - MessageChunk::Text(s) | MessageChunk::Link { title: s, .. } => { - let text_chunk = - egui::RichText::new(s).with_styles(styles, &state.settings); - if let MessageChunk::Link { - location, protocol, .. - } = c - { - let make_regular_link = - |ui: &mut egui::Ui, chunk: &egui::RichText, loc: &str| { - ui.hyperlink_to(chunk.to_owned(), loc.to_owned()) - .context_menu(|ui| { - if ui.button("Copy URL").clicked() { - ui.ctx().output_mut(|o| { - o.copied_text = loc.to_owned(); - }); - ui.close_menu(); - } - }); - }; - - if let LinkType::OSU(osu_action) = protocol { + MessageChunk::Text(text) => { + let display_text = + egui::RichText::new(text).with_styles(styles, &state.settings); + ui.label(display_text); + } + MessageChunk::Link { + title, + location, + link_type, + } => { + let display_text = + egui::RichText::new(title).with_styles(styles, &state.settings); + let make_regular_link = + |ui: &mut egui::Ui, text: &egui::RichText, loc: &str| { + ui.hyperlink_to(text.to_owned(), loc.to_owned()) + .context_menu(|ui| { + if ui.button("Copy URL").clicked() { + ui.ctx().output_mut(|o| { + o.copied_text = loc.to_owned(); + }); + ui.close_menu(); + } + }); + }; + match link_type { + LinkType::HTTP | LinkType::HTTPS => { + make_regular_link(ui, &display_text, &location); + } + LinkType::OSU(osu_action) => { match osu_action { Action::Chat(chat) => { match state.settings.chat.behaviour.handle_osu_chat_links { true => { if ui - .link(text_chunk) + .link(display_text) .on_hover_text_at_pointer( match chat.is_channel() { true => format!("Open {}", chat), @@ -605,7 +613,7 @@ fn format_chat_message_text( } } } - false => make_regular_link(ui, &text_chunk, location), + false => make_regular_link(ui, &display_text, location), } } Action::OpenBeatmap(beatmap_id) => { @@ -617,7 +625,7 @@ fn format_chat_message_text( beatmap_id ); if ui - .link(text_chunk) + .link(display_text) .on_hover_text_at_pointer(format!( "Beatmap #{} (open in browser)", beatmap_id @@ -638,7 +646,7 @@ fn format_chat_message_text( }); } } - false => make_regular_link(ui, &text_chunk, location), + false => make_regular_link(ui, &display_text, location), } } Action::OpenDifficulty(difficulty_id) => { @@ -650,7 +658,7 @@ fn format_chat_message_text( difficulty_id ); if ui - .link(text_chunk) + .link(display_text) .on_hover_text_at_pointer(format!( "Beatmap difficulty #{} (open in browser)", difficulty_id @@ -671,19 +679,28 @@ fn format_chat_message_text( }); } } - false => make_regular_link(ui, &text_chunk, location), + false => make_regular_link(ui, &display_text, location), } } // TODO: find a use for this Action::Multiplayer(_lobby_id) => { - make_regular_link(ui, &text_chunk, location); + make_regular_link(ui, &display_text, location); + } + } + } + LinkType::Channel => { + if ui + .link(display_text) + .on_hover_text_at_pointer(format!("Open {}", location)) + .clicked() + { + if state.has_chat(location) { + state.core.chat_switch_requested(location, None); + } else { + state.core.channel_join_requested(location); } } - } else { - make_regular_link(ui, &text_chunk, location); } - } else { - ui.label(text_chunk); } } }