-
Notifications
You must be signed in to change notification settings - Fork 40
Add image viewer widget #565
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
base: main
Are you sure you want to change the base?
Conversation
91bf416 to
440bde3
Compare
|
Intention of adding an image magnifier and the ability to pan image. The image's image_scale, image_pan will always be set to default in draw_walk. Hence unable to dynamically scale and pan image. |
1b7875f to
ddd04b9
Compare
@alanpoon I just checked with Rik, and this line should not be present in the Image widget. It's some errant code left over from dealing with animations in a strange way. You can submit a PR to makepad that removes that line, and then continue with this issue here. |
|
Sure, the makepad PR is here: makepad/makepad#788 |
kevinaboos
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Alan, nice work here.
My main comment is that we should decouple the ImageViewer widget from the RoomScreen. It doesn't need to know anything about the RoomScreen except for being able to access the RoomScreen's MediaCache instance (technically even that is not required, see Aarav's PR for an example of just passing the image data directly from the media fetch background task to the ImageViewer via an action).
Also, Aarav and I had a lot of discussions about using actions to communicate with the ImageViewer widget in PR#443. I think you can combine your approach (of storing images in the RoomScreen's MediaCache) with the action-based design from #443 (in which you emit an action including the image data/status instead of directly accessing the widget and calling a function on it). Actions are more idiomatic and also allow easy communication from any context, both in a background task and in a RoomScreen TimelineUpdate handler.
kevinaboos
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code looks pretty good, I left mostly nitpick comments but a few are more significant in nature.
src/home/room_image_viewer.rs
Outdated
| /// Extracts image name and size from an event timeline item. | ||
| pub fn extract_image_info(event_tl_item: &EventTimelineItem) -> (String, i32) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- "Extract" makes it sound like you're unarchiving/decompressing a file. I would just call it something like
get_image_name_and_filesizeor something similar. - The image "size" sounds like a dimension, i'd recommend saying "size in bytes" or something just to be very clear.
- How can an image size value be a signed integer? Answer: it cannot, and in the SDK it's a
UInt, so you can either use that directly or convert it to au64.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/home/room_image_viewer.rs
Outdated
| } else { | ||
| ("Unknown Image".to_string(), 0) | ||
| } | ||
| } else { | ||
| ("Unknown Image".to_string(), 0) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dedup these, e.g., by returning early from within the if conditional and then having this as a default return value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
src/home/room_image_viewer.rs
Outdated
| /// Condensed message does not have a profile, so we need to find the previous portal list item. | ||
| /// Searches backwards for a non-empty display name and avatar in previous portal list items. | ||
| /// Mutates display_name and avatar_ref. | ||
| /// Returns when first non-empty display name is found. | ||
| pub fn find_previous_profile_in_condensed_message( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Reading widget values from the UI is an odd way to try to obtain content for a timeline event. I'm not sure what context you have when calling this, but why not just get this info from the event timeline item itself?
- Using this style of output parameters is strange and not very rusty. I'd recommend you just return the display_name and the AvatarRef directly instead of mutating them, because that would clearly convey when you were unable to obtain them. Currently, with this function signature, there's no way to know if you successfully obtained those two bits of data, which is a form of silencing errors.
- Nit: doc comments are rendered using markdown, so the line separation here gets lost. Generally, you should start the doc comment with a short one-line summary, then a blank line, and then the details in any format you wish.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1-2: Removed this function, and use avatar.set_avatar_and_get_username after retrieving timeline event.
3: Doc fixed.
src/home/room_screen.rs
Outdated
| index: usize, | ||
| widget_ref: WidgetRef, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you give these more specific names? I assume index is the index into the portallist of ... something? and what is widget_ref? That could be anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed index to item_id. Removed widget_ref.
src/shared/image_viewer.rs
Outdated
| /// The error type is matched against a string which describes the error in a way that is visible to the user. | ||
| /// | ||
| /// The strings returned by this function will be appropriate for display in a label or similar widget. | ||
| pub fn image_viewer_error_to_string(error: &ImageViewerError) -> &str { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check out the thiserror crate, that's exactly what it's for. You're re-creating a less featureful version of proper error conversion/display.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix by using thiserror crate.
src/shared/image_viewer.rs
Outdated
| } | ||
| } | ||
| } | ||
| pub Rotation_Button = <RobrixIconButton> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this doesn't follow typical code style, you're combining camel case and snake case. It should be something like RotateButton.
src/shared/image_viewer.rs
Outdated
| flow: Right | ||
|
|
||
| top_left_container = <View> { | ||
| width: 150, height: Fit, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how does this size choice look with longer usernames? 150 pixels seems way too small to fit an avatar, a username, and a timestamp
and what about mobile devices with skinnier screens? Don't forget to test things on a wide variety of view sizes.
Honestly it seems like you might need to rethink this layout. Perhaps something like image name on the top, and then the avatar+username+timestamp on the line beneath that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed top_left_container's width to be 200. Added the logic to empty_right_container's width to 0 when display_context is mobile.
src/shared/image_viewer.rs
Outdated
| /// A lock to prevent multiple rotation animations from running at the same time | ||
| #[rust] | ||
| is_animating_rotation: bool, | ||
| #[animator] | ||
| animator: Animator, | ||
| /// Timer for rotation animation, prevents clicking the rotation buttons too often | ||
| /// to start the animation | ||
| #[rust] | ||
| timer: Timer, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using a "lock" (which is just a bool, not really a lock) plus a timer to prevent multiple simultaneous animations is a very fragile way to achieve this.
Luckily, you don't need any of this. All you have to do is check the state of the animator, and if it is playing an animator, simply ignore additional button presses on the rotate buttons.
Alternatively, if the user clicks the rotate button while the image is already being rotated, you could just cut the animation short and then jump to the next rotation state immediately, and then start another rotation animation again. That is probably a way better UX than some kind of hidden timeout thing.
In general, timers in makepad aren't great and we definitely should not use them for anything related to animations, ever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to use is_animating.
src/shared/image_viewer.rs
Outdated
| // Clear the rotated image texture with a white background | ||
| if let Ok(image_buffer) = ImageBuffer::new(&[255], 1, 1) { | ||
| let texture = image_buffer.into_new_texture(cx); | ||
| self.view | ||
| .rotated_image(ids!(rotated_image)) | ||
| .set_texture(cx, Some(texture.clone())); | ||
| self.view.rotated_image(ids!(rotated_image)).redraw(cx); | ||
| self.view | ||
| .image(ids!(metadata_view.top_left_container.avatar.img_view.img)) | ||
| .set_texture(cx, Some(texture)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it actually necessary to clear the image texture? seems like wasted effort because the image texture will be set/replaced later anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you need to free the texture's memory, just do that. No need to create a new one and actually draw it.
Co-authored-by: Kevin Boos <[email protected]>
Co-authored-by: Kevin Boos <[email protected]>
…into image_viewer#327
238c3e2 to
249a384
Compare
3f5eaca to
0c1fb66
Compare



Fixes #327

in replacement of #443
Waiting for this PR to be merged: makepad/makepad#788.