Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When the user clicks a thumbnail image, show the full-size image in an image viewer widget #341

Open
wants to merge 67 commits into
base: main
Choose a base branch
from

Conversation

aaravlu
Copy link
Contributor

@aaravlu aaravlu commented Jan 22, 2025

@aaravlu aaravlu changed the title Fix327 When the user clicks a thumbnail image, show the full-size image in an image viewer widget Jan 22, 2025
@aaravlu aaravlu requested a review from kevinaboos January 22, 2025 04:38
@alanpoon
Copy link
Contributor

There is no close button for Image previewer. Only the button to close the entire program.
https://github.com/user-attachments/assets/d0a2db15-1ee4-42a7-8756-46d5fd031408

@alanpoon alanpoon added the waiting-on-author This issue is waiting on the original author for a response label Jan 23, 2025
@aaravlu
Copy link
Contributor Author

aaravlu commented Jan 23, 2025

There is no close button for Image previewer. Only the button to close the entire program. https://github.com/user-attachments/assets/d0a2db15-1ee4-42a7-8756-46d5fd031408

Yeah, many bugs currently, all the widgetsabout image viewer, I set absolutly position, so maybe you cannot see the button.

Now it's better, but still some bugs :(
Screencast from 2025-01-23 11-49-27.webm

@aaravlu
Copy link
Contributor Author

aaravlu commented Jan 24, 2025

  • We cannot set height or width Fit for <ImageViewerModal>, because it doesn't know how long or wide the image is, it is waiting for the image's actual length or width, but the image has not been fetched yet.

width: 1600, height: 900

  • A widget wrapped by <Modal> cannot handle_actions it self when the <Modal> is closed. I do this in app.rs so it can be avoided.

match action.downcast_ref() {

  • After a large size image being fetched and filled to image viewer, we cannot see the image, I guess it cousts too large amout of time fetching the image to "forget" to redraw, so that it cannot be seen.

  • Fetching image to image viewer can block ui threads, I have no idea for this currently.

We can keep this pr open and I shall put my energies elsewhere issues, IMHO

@aaravlu aaravlu added blocked-on-makepad Blocked on a Makepad bug or missing Makepad feature and removed waiting-on-author This issue is waiting on the original author for a response labels Jan 24, 2025
@kevinaboos
Copy link
Member

  • We cannot set height or width Fit for <ImageViewerModal>, because it doesn't know how long or wide the image is, it is waiting for the image's actual length or width, but the image has not been fetched yet.

Right, but in general, the Matrix SDK can provide image metadata including dimensions, so you can use that as a placeholder until you download the entire image. Even if you don't know the size of the image, you can just show the thumbnail image in the ImageViewer widget until the full-size image has been fully-fetched, and then you will know the size of the full image.

Regardless, it should not matter what the image's size is. The ImageViewer should be full-screen (width: Fill, height: Fill), and the Image fit mode can be set to fit: Biggest (or Smallest, based on your desired choice).

  • A widget wrapped by <Modal> cannot handle_actions it self when the <Modal> is closed. I do this in app.rs so it can be avoided.

You're correct, the Modal widget doesn't. We can modify it, but in general, the Modal widget has lots of issues right now. Don't use the Modal widget; you can copy my LoadingPane widget instead.

  • After a large size image being fetched and filled to image viewer, we cannot see the image, I guess it cousts too large amout of time fetching the image to "forget" to redraw, so that it cannot be seen.

I don't understand what this means, sorry.

  • Fetching image to image viewer can block ui threads, I have no idea for this currently.

Yes, of course, you'll have to do it on a background thread/task. We already have the infrastructure for this.

In summary, it sounds like you're a bit stuck on this, which is totally fine. Thanks for submitting your work thus far; in the future, I (or others) can help you complete the ImageViewer widget.

@kevinaboos kevinaboos added help wanted Looking for help from anyone! and removed blocked-on-makepad Blocked on a Makepad bug or missing Makepad feature labels Jan 24, 2025
@aaravlu
Copy link
Contributor Author

aaravlu commented Feb 24, 2025

I removed the spin loader in this pr while would take it back if #389 is OK.

lastest fix:
Screencast from 2025-02-24 10-53-01.webm

@aaravlu aaravlu added waiting-on-author This issue is waiting on the original author for a response and removed waiting-on-review This issue is waiting to be reviewed labels Feb 24, 2025
@aaravlu aaravlu added waiting-on-review This issue is waiting to be reviewed and removed waiting-on-author This issue is waiting on the original author for a response labels Feb 24, 2025
@kevinaboos
Copy link
Member

All the media's decoding can be put into sliding_syc to leave it to tokio to auto handle to avoid blocking ui-thread

Yes, we have planned to do this for all images for a while now, just haven't gotten around to it. The general idea of offloading image decoding to a background thread has been floating around our discussions for a few months at least, and yes, we should absolutely do it -- but not just for the ImageViewer, for all images. Let's keep that as part of a separate PR though, in the future.

Copy link
Member

@kevinaboos kevinaboos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The design of the ImageViewer widget itself is way too complicated. We don't need a fancy BTreeMap or a MediaCache within it --- the only thing it needs to include is a simple <Image> or <TextOrImage> instance. This is because the actual image data (for the image that will be shown) will come from another part of the code, so we don't need to store anything within the ImageViewer instance itself.

match media_cache.try_get_media_or_fetch(mxc_uri.clone(), Some(MEDIA_THUMBNAIL_FORMAT.into())) {
MediaCacheEntry::Loaded(data) => {

let mut fetch_and_show_image_uri = |cx: &mut Cx2d, original_source: MediaSource, mxc_uri: OwnedMxcUri, image_info: Option<&ImageInfo>| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that we have multiple different image "sources", we need to name them more clearly. What is original_source? Is that the source info for the full-sized image?

And then what is mxc_uri for? We need to name them better so we know what each one means.

Same question for image_info -- which image does that apply to?


if let MediaSource::Plain(original_uri) = original_source {
let original_thumbnail = OriginalAndThumbnail::new(original_uri, thumbnail_data);
Cx::post_action(ImageViewerAction::SetData {text_or_image_uid, original_thumbnail});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need to pass in the widget UID of the TextOrImage instance? that doesn't make any sense --- there's only one ImageViewer, so this widget UID will never be of any use.

@@ -3452,7 +3459,10 @@ fn populate_image_message_content(
}
};

let mut fetch_and_show_media_source = |cx: &mut Cx2d, media_source: MediaSource, image_info: Option<&ImageInfo>| {

let mut fetch_and_show_media_source = |cx: &mut Cx2d, original_source: MediaSource, thumbnail_source: Option<MediaSource>, image_info: Option<&ImageInfo>| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment here about naming the arguments.

.and_then(|image_info| image_info.thumbnail_source)
.unwrap_or(original_source);
fetch_and_show_media_source(cx, media_source, image_info.as_ref());
let thumbnail_source = image_info.clone()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to repeatedly (and eagerly) clone all of the image info? That seems unnecessary. Couldn't you just pass in a reference to all of these image_info instances?

Comment on lines 71 to 76
/// Key is uid of `TextOrImage`, val is the corresponded image uri and its thumbnail data.
#[rust]
map: BTreeMap<WidgetUid, OriginalAndThumbnail>,
/// We use a standalone `MediaCache` to store the original image data.
#[rust]
media_cache: MediaCache,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a strange approach for designing this widget. It should literally just contain a single image instance (maybe a TextOrImage instead, if we want to show a loading message, which would be a good idea), and nothing else. This widget should be very simple, no need to overcomplicate it.

We don't need this weird map or a media_cache here at all. Including those do not make any sense, because that information is already available from the room's timeline message or user avatar that actually provided the image source.

@kevinaboos kevinaboos added waiting-on-author This issue is waiting on the original author for a response and removed waiting-on-review This issue is waiting to be reviewed labels Feb 24, 2025
@aaravlu aaravlu mentioned this pull request Feb 26, 2025
@aaravlu aaravlu added blocked Blocked on another issue or missing feature and removed waiting-on-author This issue is waiting on the original author for a response labels Feb 26, 2025
@aaravlu
Copy link
Contributor Author

aaravlu commented Feb 26, 2025

need to refactor MediaCache, blocked by #405

@aaravlu aaravlu added waiting-on-author This issue is waiting on the original author for a response and removed blocked Blocked on another issue or missing feature labels Mar 13, 2025
@aaravlu aaravlu added waiting-on-review This issue is waiting to be reviewed and removed waiting-on-author This issue is waiting on the original author for a response labels Mar 13, 2025
let mut fetch_and_show_image_uri = |cx: &mut Cx2d, mxc_uri: OwnedMxcUri, image_info: Option<&ImageInfo>| {
match media_cache.try_get_media_or_fetch(mxc_uri.clone(), MEDIA_THUMBNAIL_FORMAT.into()) {
//
// I refactor those two closure here, feel free to renamed it.
Copy link
Contributor Author

@aaravlu aaravlu Mar 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinaboos , Just feel free to rename this closure here.

@aaravlu aaravlu requested a review from kevinaboos March 13, 2025 05:38
@aaravlu aaravlu added waiting-on-author This issue is waiting on the original author for a response and removed waiting-on-review This issue is waiting to be reviewed labels Mar 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting-on-author This issue is waiting on the original author for a response
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants