From 7fe0955463e21606d5ebc9d3b3a3ea9db641d4c1 Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Fri, 22 Nov 2024 13:36:02 -0800 Subject: [PATCH 1/3] Display Notice and Emote messages properly * Notice: username and text body color is a lighter gray than regular message content from real users. * Emote: the body prepended by a "* ", as suggested by the Matrix spec. --- src/home/room_screen.rs | 121 ++++++++++++++++++++++++++++---- src/home/welcome_screen.rs | 2 +- src/shared/html_or_plaintext.rs | 21 +++--- src/shared/styles.rs | 5 +- 4 files changed, 125 insertions(+), 24 deletions(-) diff --git a/src/home/room_screen.rs b/src/home/room_screen.rs index 24c9b67c..8f8a5649 100644 --- a/src/home/room_screen.rs +++ b/src/home/room_screen.rs @@ -9,7 +9,7 @@ use matrix_sdk::{ ruma::{ events::room::{ message::{ - FormattedBody, ImageMessageEventContent, LocationMessageEventContent, MessageFormat, MessageType, NoticeMessageEventContent, RoomMessageEventContent, TextMessageEventContent + EmoteMessageEventContent, FormattedBody, ImageMessageEventContent, LocationMessageEventContent, MessageFormat, MessageType, NoticeMessageEventContent, RoomMessageEventContent, TextMessageEventContent }, MediaSource, }, matrix_uri::MatrixId, uint, EventId, MatrixToUri, MatrixUri, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, UserId @@ -37,6 +37,8 @@ use super::loading_modal::{LoadingModalAction, LoadingModalState}; const GEO_URI_SCHEME: &str = "geo:"; +const MESSAGE_NOTICE_TEXT_COLOR: Vec3 = Vec3 { x: 0.5, y: 0.5, z: 0.5 }; + live_design! { import makepad_draw::shader::std::*; import makepad_widgets::base::*; @@ -227,7 +229,7 @@ live_design! { height: Fit, padding: {top: 5.0} - html_content = { + html_content = { width: Fill, height: Fit, padding: { bottom: 5.0, top: 0.0 }, @@ -2375,6 +2377,8 @@ fn populate_message_view( let ts_millis = event_tl_item.timestamp(); + let mut is_notice = false; // whether this message is a Notice + // Determine whether we can use a more compact UI view that hides the user's profile info // if the previous message was sent by the same user within 10 minutes. let use_compact_view = match prev_event.map(|p| p.kind()) { @@ -2391,9 +2395,67 @@ fn populate_message_view( _ => false, }; + // Sometimes we need to call this up-front, so we save the result in this variable + // to avoid having to call it twice. + let mut set_username_and_get_avatar_retval = None; + let (item, used_cached_item) = match message.msgtype() { - MessageType::Text(TextMessageEventContent { body, formatted, .. }) - | MessageType::Notice(NoticeMessageEventContent { body, formatted, .. }) => { + MessageType::Text(TextMessageEventContent { body, formatted, .. }) => { + let template = if use_compact_view { + live_id!(CondensedMessage) + } else { + live_id!(Message) + }; + let (item, existed) = list.item_with_existed(cx, item_id, template); + if existed && item_drawn_status.content_drawn { + (item, true) + } else { + populate_text_message_content( + &item.html_or_plaintext(id!(content.message)), + &body, + formatted.as_ref(), + ); + new_drawn_status.content_drawn = true; + (item, false) + } + } + // A notice message is just a message sent by an automated bot, + // so we treat it just like a message but use a different font color. + MessageType::Notice(NoticeMessageEventContent { body, formatted, .. }) => { + is_notice = true; + let template = if use_compact_view { + live_id!(CondensedMessage) + } else { + live_id!(Message) + }; + let (item, existed) = list.item_with_existed(cx, item_id, template); + if existed && item_drawn_status.content_drawn { + (item, true) + } else { + let html_or_plaintext_ref = item.html_or_plaintext(id!(content.message)); + html_or_plaintext_ref.apply_over(cx, live!( + html_view = { + html = { + font_color: (MESSAGE_NOTICE_TEXT_COLOR), + draw_normal: { color: (MESSAGE_NOTICE_TEXT_COLOR), } + draw_italic: { color: (MESSAGE_NOTICE_TEXT_COLOR), } + draw_bold: { color: (MESSAGE_NOTICE_TEXT_COLOR), } + draw_bold_italic: { color: (MESSAGE_NOTICE_TEXT_COLOR), } + } + } + )); + populate_text_message_content( + &html_or_plaintext_ref, + &body, + formatted.as_ref(), + ); + new_drawn_status.content_drawn = true; + (item, false) + } + } + // An emote is just like a message but is prepended with the user's name + // to indicate that it's an "action" that the user is performing. + MessageType::Emote(EmoteMessageEventContent { body, formatted, .. }) => { let template = if use_compact_view { live_id!(CondensedMessage) } else { @@ -2403,11 +2465,34 @@ fn populate_message_view( if existed && item_drawn_status.content_drawn { (item, true) } else { + // Draw the profile up front here because we need the username for the emote body. + let (username, profile_drawn) = set_avatar_and_get_username( + cx, + item.avatar(id!(profile.avatar)), + room_id, + event_tl_item.sender(), + event_tl_item.sender_profile(), + event_tl_item.event_id(), + ); + + // Prepend a "* " to the emote body, as suggested by the Matrix spec. + let (body, formatted) = if let Some(fb) = formatted.as_ref() { + ( + Cow::from(&fb.body), + Some(FormattedBody { + format: fb.format.clone(), + body: format!("* {} {}", &username, &fb.body), + }) + ) + } else { + (Cow::from(format!("* {} {}", &username, body)), None) + }; populate_text_message_content( &item.html_or_plaintext(id!(content.message)), &body, formatted.as_ref(), ); + set_username_and_get_avatar_retval = Some((username, profile_drawn)); new_drawn_status.content_drawn = true; (item, false) } @@ -2490,15 +2575,25 @@ fn populate_message_view( new_drawn_status.profile_drawn = true; } else { // log!("\t --> populate_message_view(): DRAWING profile draw for item_id: {item_id}"); - let (username, profile_drawn) = set_avatar_and_get_username( - cx, - item.avatar(id!(profile.avatar)), - room_id, - event_tl_item.sender(), - event_tl_item.sender_profile(), - event_tl_item.event_id(), + let (username, profile_drawn) = set_username_and_get_avatar_retval.unwrap_or_else(|| + set_avatar_and_get_username( + cx, + item.avatar(id!(profile.avatar)), + room_id, + event_tl_item.sender(), + event_tl_item.sender_profile(), + event_tl_item.event_id(), + ) ); - item.label(id!(content.username)).set_text(&username); + let username_label = item.label(id!(content.username)); + if is_notice { + username_label.apply_over(cx, live!( + draw_text: { + color: (MESSAGE_NOTICE_TEXT_COLOR), + } + )); + } + username_label.set_text(&username); new_drawn_status.profile_drawn = profile_drawn; } @@ -2556,7 +2651,7 @@ fn populate_text_message_content( body: &str, formatted_body: Option<&FormattedBody>, ) { - if let Some(formatted_body) = formatted_body + if let Some(formatted_body) = formatted_body.as_ref() .and_then(|fb| (fb.format == MessageFormat::Html).then(|| fb.body.clone())) { message_content_widget.show_html(utils::linkify(formatted_body.as_ref())); diff --git a/src/home/welcome_screen.rs b/src/home/welcome_screen.rs index 5120b591..f51a8cd0 100644 --- a/src/home/welcome_screen.rs +++ b/src/home/welcome_screen.rs @@ -29,7 +29,7 @@ live_design! { } // Using the HTML widget to taking advantage of embedding a link within text with proper vertical alignment - { + { padding: {top: 12, left: 0.} font_size: 14. font_color: (WELCOME_TEXT_COLOR) diff --git a/src/shared/html_or_plaintext.rs b/src/shared/html_or_plaintext.rs index aae62292..b82c09a9 100644 --- a/src/shared/html_or_plaintext.rs +++ b/src/shared/html_or_plaintext.rs @@ -32,7 +32,7 @@ live_design! { // A centralized widget where we define styles and custom elements for HTML // message content. This is a wrapper around Makepad's built-in `Html` widget. - RobrixHtml = { + MessageHtml = { padding: 0.0, width: Fill, height: Fit, // see comment in `HtmlOrPlaintext` font_size: (MESSAGE_FONT_SIZE), @@ -59,19 +59,22 @@ live_design! { a = { padding: {left: 1.0, right: 1.5}, - // draw_text: { - // text_style: { font_size: (MESSAGE_FONT_SIZE), height_factor: (HTML_TEXT_HEIGHT_FACTOR), line_spacing: (HTML_LINE_SPACING), top_drop: 1.2, }, - // color: #00f, - // color_pressed: #f00, - // color_hover: #0f0, - // } } body: "[ HTML message placeholder]", } + // A version of `MessageHtml` that's used for notices (automated messages from bots). + NoticeHtml = { + font_color: (MESSAGE_NOTICE_TEXT_COLOR), + draw_normal: { color: (MESSAGE_NOTICE_TEXT_COLOR), } + draw_italic: { color: (MESSAGE_NOTICE_TEXT_COLOR), } + draw_bold: { color: (MESSAGE_NOTICE_TEXT_COLOR), } + draw_bold_italic: { color: (MESSAGE_NOTICE_TEXT_COLOR), } + } + // A view container that displays either plaintext s(a simple `Label`) - // or rich HTML content (an instance of `RobrixHtml`). + // or rich HTML content (an instance of `MessageHtml`). // // Key Usage Notes: // * Labels need their width to be Fill *and* all of their parent views @@ -101,7 +104,7 @@ live_design! { html_view = { visible: false, width: Fill, height: Fit, // see above comment - html = {} + html = {} } } } diff --git a/src/shared/styles.rs b/src/shared/styles.rs index 59309264..82097d60 100644 --- a/src/shared/styles.rs +++ b/src/shared/styles.rs @@ -31,7 +31,9 @@ live_design! { TYPING_NOTICE_TEXT_COLOR = #121570 MESSAGE_FONT_SIZE = 11 - MESSAGE_TEXT_COLOR = #x444 + MESSAGE_TEXT_COLOR = #x333 + // notices (automated messages from bots) use a lighter color + MESSAGE_NOTICE_TEXT_COLOR = #x888 MESSAGE_TEXT_LINE_SPACING = 1.35 MESSAGE_TEXT_HEIGHT_FACTOR = 1.55 // This font should only be used for plaintext labels. Don't use this for Html content, @@ -44,6 +46,7 @@ live_design! { MESSAGE_REPLY_PREVIEW_FONT_SIZE = 9.5 + SMALL_STATE_FONT_SIZE = 9.0 SMALL_STATE_TEXT_COLOR = #x888 SMALL_STATE_TEXT_STYLE = { From 9a5c649f3d1bfcab68e6db27eaba6e43f9b7280a Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Fri, 22 Nov 2024 14:14:46 -0800 Subject: [PATCH 2/3] remove unused Notice-specific variante of `MessageHtml` turns out we cannot actually use this in Makepad `apply_over()` `live!()` blocks --- src/shared/html_or_plaintext.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/shared/html_or_plaintext.rs b/src/shared/html_or_plaintext.rs index b82c09a9..03c81d32 100644 --- a/src/shared/html_or_plaintext.rs +++ b/src/shared/html_or_plaintext.rs @@ -64,15 +64,6 @@ live_design! { body: "[ HTML message placeholder]", } - // A version of `MessageHtml` that's used for notices (automated messages from bots). - NoticeHtml = { - font_color: (MESSAGE_NOTICE_TEXT_COLOR), - draw_normal: { color: (MESSAGE_NOTICE_TEXT_COLOR), } - draw_italic: { color: (MESSAGE_NOTICE_TEXT_COLOR), } - draw_bold: { color: (MESSAGE_NOTICE_TEXT_COLOR), } - draw_bold_italic: { color: (MESSAGE_NOTICE_TEXT_COLOR), } - } - // A view container that displays either plaintext s(a simple `Label`) // or rich HTML content (an instance of `MessageHtml`). // From 9f125deff809952bed33ea9c5ca0a601d706d47f Mon Sep 17 00:00:00 2001 From: Kevin Boos Date: Fri, 22 Nov 2024 14:15:31 -0800 Subject: [PATCH 3/3] Use a darker background for code/quote blocks in a selected RoomPreview The default lighter gray color can be quite difficult to see due to low contrast. --- src/home/room_preview.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/home/room_preview.rs b/src/home/room_preview.rs index c330fd4f..d1fa5c20 100644 --- a/src/home/room_preview.rs +++ b/src/home/room_preview.rs @@ -255,6 +255,7 @@ impl RoomPreviewContent { let message_text_color; let room_name_color; let timestamp_color; + let code_bg_color; // TODO: This is quite verbose, makepad should provide a way to override this at a higher level. if is_selected { @@ -262,11 +263,13 @@ impl RoomPreviewContent { message_text_color = vec3(1., 1., 1.); // COLOR_PRIMARY room_name_color = vec3(1., 1., 1.); // COLOR_PRIMARY timestamp_color = vec3(1., 1., 1.); // COLOR_PRIMARY + code_bg_color = vec3(0.3, 0.3, 0.3); // a darker gray, used for `code_color` and `quote_bg_color` } else { bg_color = vec3(1., 1., 1.); // COLOR_PRIMARY message_text_color = vec3(0.267, 0.267, 0.267); // MESSAGE_TEXT_COLOR room_name_color = vec3(0., 0., 0.); timestamp_color = vec3(0.6, 0.6, 0.6); + code_bg_color = vec3(0.929, 0.929, 0.929); // #EDEDED, see `code_color` and `quote_bg_color` } self.view.apply_over( @@ -312,6 +315,10 @@ impl RoomPreviewContent { draw_italic: { color: (message_text_color) }, draw_bold: { color: (message_text_color) }, draw_bold_italic: { color: (message_text_color) }, + draw_block: { + quote_bg_color: (code_bg_color), + code_color: (code_bg_color), + } } } plaintext_view = {