diff --git a/Cargo.lock b/Cargo.lock
index 663b2c5..ddbfb05 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1362,11 +1362,11 @@ dependencies = [
[[package]]
name = "directories"
-version = "5.0.1"
+version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
+checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
dependencies = [
- "dirs-sys",
+ "dirs-sys 0.5.0",
]
[[package]]
@@ -1375,7 +1375,7 @@ version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
- "dirs-sys",
+ "dirs-sys 0.4.1",
]
[[package]]
@@ -1386,10 +1386,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
- "redox_users",
+ "redox_users 0.4.6",
"windows-sys 0.48.0",
]
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users 0.5.0",
+ "windows-sys 0.59.0",
+]
+
[[package]]
name = "dispatch"
version = "0.2.0"
@@ -1512,7 +1524,7 @@ dependencies = [
[[package]]
name = "egui-video"
version = "0.8.0"
-source = "git+https://github.com/v0l/egui-video.git?rev=d2ea3b4db21eb870a207db19e4cd21c7d1d24836#d2ea3b4db21eb870a207db19e4cd21c7d1d24836"
+source = "git+https://github.com/v0l/egui-video.git?rev=11db7d0c30070529a36bfb050844cdb75c32902b#11db7d0c30070529a36bfb050844cdb75c32902b"
dependencies = [
"anyhow",
"atomic",
@@ -4517,6 +4529,17 @@ dependencies = [
"thiserror 1.0.69",
]
+[[package]]
+name = "redox_users"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror 2.0.9",
+]
+
[[package]]
name = "regex"
version = "1.11.0"
diff --git a/Cargo.toml b/Cargo.toml
index 7f89de3..9f48582 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,8 +18,8 @@ bech32 = "0.11.0"
anyhow = "^1.0.91"
itertools = "0.14.0"
serde = { version = "1.0.214", features = ["derive"] }
-directories = "5.0.1"
-egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "d2ea3b4db21eb870a207db19e4cd21c7d1d24836" }
+directories = "6.0.0"
+egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "11db7d0c30070529a36bfb050844cdb75c32902b" }
egui_qr = { git = "https://git.v0l.io/Kieran/egui_qr.git", rev = "f9cf52b7eae353fa9e59ed0358151211d48824d1" }
# notedeck stuff
diff --git a/src/app.rs b/src/app.rs
index e47672f..cebe944 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -62,6 +62,12 @@ impl ZapStreamApp {
.insert(FontFamily::Proportional, vec!["Outfit".to_string()]);
cc.egui_ctx.set_fonts(fd);
+ // ffmpeg log redirect
+ unsafe {
+ egui_video::ffmpeg_sys_the_third::av_log_set_callback(Some(
+ egui_video::ffmpeg_rs_raw::av_log_redirect,
+ ));
+ }
let (tx, rx) = mpsc::channel();
Self {
current: RouteType::HomePage,
@@ -103,6 +109,8 @@ impl notedeck::App for ZapStreamApp {
},
);
+ //ui.ctx().set_debug_on_hover(true);
+
let app_frame = egui::containers::Frame::default().outer_margin(self.frame_margin());
// handle app state changes
diff --git a/src/bin/zap_stream_app.rs b/src/bin/zap_stream_app.rs
index 10bf746..417d8fe 100644
--- a/src/bin/zap_stream_app.rs
+++ b/src/bin/zap_stream_app.rs
@@ -2,7 +2,7 @@ use anyhow::Result;
use directories::ProjectDirs;
use eframe::Renderer;
use egui::{Vec2, ViewportBuilder};
-use log::error;
+use log::{error, info};
use zap_stream_app::app::ZapStreamApp;
#[tokio::main]
@@ -13,11 +13,12 @@ async fn main() -> Result<()> {
options.viewport = ViewportBuilder::default().with_inner_size(Vec2::new(1300., 900.));
options.renderer = Renderer::Glow;
- let data_path = ProjectDirs::from("stream", "zap", "app")
+ let data_path = ProjectDirs::from("stream", "zap", "zap_stream_app")
.unwrap()
- .config_dir()
+ .data_dir()
.to_path_buf();
+ info!("Data path: {}", data_path.display());
if let Err(e) = eframe::run_native(
"zap.stream",
options,
diff --git a/src/resources/logo.svg b/src/resources/logo.svg
index e11504f..3f9a9b1 100644
--- a/src/resources/logo.svg
+++ b/src/resources/logo.svg
@@ -1,13 +1,4 @@
diff --git a/src/route/home.rs b/src/route/home.rs
index 6e7ddf9..c94c0b8 100644
--- a/src/route/home.rs
+++ b/src/route/home.rs
@@ -23,7 +23,7 @@ impl HomePage {
}
fn get_filters() -> Vec {
- vec![Filter::new().kinds([30_311]).limit(100).build()]
+ vec![Filter::new().kinds([30_311, 30_313]).limit(100).build()]
}
}
diff --git a/src/route/mod.rs b/src/route/mod.rs
index 22828d4..e51b79b 100644
--- a/src/route/mod.rs
+++ b/src/route/mod.rs
@@ -3,7 +3,8 @@ use crate::services::ffmpeg_loader::FfmpegLoader;
use crate::widgets::PlaceholderRect;
use anyhow::{anyhow, bail};
use egui::load::SizedTexture;
-use egui::{Context, Id, Image, ImageSource, TextureHandle, Ui};
+use egui::{Context, Id, Image, ImageSource, TextureHandle, Ui, Vec2};
+use egui_video::ffmpeg_rs_raw::Transcoder;
use ehttp::Response;
use enostr::EventClientMessage;
use lnurl::lightning_address::LightningAddress;
@@ -187,18 +188,31 @@ impl<'a, 'ctx> RouteServices<'a, 'ctx> {
}
}
-const BLACK_PIXEL: [u8; 4] = [0, 0, 0, 0];
+const LOGO_BYTES: &[u8] = include_bytes!("../resources/logo.svg");
-pub fn image_from_cache<'a>(img_cache: &mut ImageCache, ui: &Ui, url: &str) -> Image<'a> {
- if let Some(promise) = img_cache.map().get(url) {
+pub fn image_from_cache<'a>(
+ img_cache: &mut ImageCache,
+ ui: &Ui,
+ url: &str,
+ size: Option,
+) -> Image<'a> {
+ let cache_key = if let Some(s) = size {
+ format!("{}:{}", url, s)
+ } else {
+ url.to_string()
+ };
+ if url.len() == 0 {
+ return Image::from_bytes(cache_key, LOGO_BYTES);
+ }
+ if let Some(promise) = img_cache.map().get(&cache_key) {
match promise.poll() {
Poll::Ready(Ok(t)) => Image::new(SizedTexture::from_handle(t)),
- _ => Image::from_bytes(url.to_string(), &BLACK_PIXEL),
+ _ => Image::from_bytes(url.to_string(), LOGO_BYTES),
}
} else {
- let fetch = fetch_img(img_cache, ui.ctx(), url);
- img_cache.map_mut().insert(url.to_string(), fetch);
- Image::from_bytes(url.to_string(), &BLACK_PIXEL)
+ let fetch = fetch_img(img_cache, ui.ctx(), url, size);
+ img_cache.map_mut().insert(cache_key.clone(), fetch);
+ Image::from_bytes(cache_key, LOGO_BYTES)
}
}
@@ -206,52 +220,48 @@ fn fetch_img(
img_cache: &ImageCache,
ctx: &Context,
url: &str,
+ size: Option,
) -> Promise> {
- let k = ImageCache::key(url);
- let dst_path = img_cache.cache_dir.join(k);
+ let name = ImageCache::key(url);
+ let dst_path = img_cache.cache_dir.join(&name);
if dst_path.exists() {
let ctx = ctx.clone();
- let url = url.to_owned();
- let dst_path = dst_path.clone();
- Promise::spawn_blocking(move || {
+ Promise::spawn_thread("load_from_disk", move || {
info!("Loading image from disk: {}", dst_path.display());
- match FfmpegLoader::new().load_image(dst_path) {
- Ok(img) => Ok(ctx.load_texture(&url, img, Default::default())),
+ match FfmpegLoader::new().load_image(dst_path, size) {
+ Ok(img) => Ok(ctx.load_texture(&name, img, Default::default())),
Err(e) => Err(notedeck::Error::Generic(e.to_string())),
}
})
} else {
- fetch_img_from_net(&dst_path, ctx, url)
+ let url = url.to_string();
+ let ctx = ctx.clone();
+ Promise::spawn_thread("load_from_net", move || {
+ let img = match fetch_img_from_net(&url).block_and_take() {
+ Ok(img) => img,
+ Err(e) => return Err(notedeck::Error::Generic(e.to_string())),
+ };
+ std::fs::create_dir_all(&dst_path.parent().unwrap()).unwrap();
+ std::fs::write(&dst_path, &img.bytes).unwrap();
+
+ info!("Loading image from net: {}", &url);
+ match FfmpegLoader::new().load_image(dst_path, size) {
+ Ok(img) => {
+ ctx.request_repaint();
+ Ok(ctx.load_texture(&name, img, Default::default()))
+ }
+ Err(e) => Err(notedeck::Error::Generic(e.to_string())),
+ }
+ })
}
}
-fn fetch_img_from_net(
- cache_path: &Path,
- ctx: &Context,
- url: &str,
-) -> Promise> {
+fn fetch_img_from_net(url: &str) -> Promise> {
let (sender, promise) = Promise::new();
let request = ehttp::Request::get(url);
- let ctx = ctx.clone();
- let cloned_url = url.to_owned();
- let cache_path = cache_path.to_owned();
+ info!("Downloaded image: {}", url);
ehttp::fetch(request, move |response| {
- let handle = response
- .and_then(|img| {
- std::fs::create_dir_all(cache_path.parent().unwrap()).unwrap();
- std::fs::write(&cache_path, &img.bytes).unwrap();
- info!("Loading image from net: {}", cloned_url);
- let img_loaded = FfmpegLoader::new()
- .load_image(cache_path)
- .map_err(|e| e.to_string())?;
-
- Ok(ctx.load_texture(&cloned_url, img_loaded, Default::default()))
- })
- .map_err(notedeck::Error::Generic);
-
- sender.send(handle);
- ctx.request_repaint();
+ sender.send(response);
});
-
promise
}
diff --git a/src/route/profile.rs b/src/route/profile.rs
index 0cedcd0..19a1dfa 100644
--- a/src/route/profile.rs
+++ b/src/route/profile.rs
@@ -35,10 +35,14 @@ impl NostrWidget for ProfilePage {
ui.spacing_mut().item_spacing.y = 8.0;
if let Some(banner) = profile.map(|p| p.banner()).flatten() {
- image_from_cache(&mut services.ctx.img_cache, ui, banner)
- .fit_to_exact_size(vec2(ui.available_width(), 360.0))
- .rounding(ROUNDING_DEFAULT)
- .ui(ui);
+ image_from_cache(
+ &mut services.ctx.img_cache,
+ ui,
+ banner,
+ Some(vec2(ui.available_width(), 360.0)),
+ )
+ .rounding(ROUNDING_DEFAULT)
+ .ui(ui);
} else {
ui.add(PlaceholderRect);
}
diff --git a/src/route/stream.rs b/src/route/stream.rs
index 3e406fb..d174e38 100644
--- a/src/route/stream.rs
+++ b/src/route/stream.rs
@@ -1,14 +1,14 @@
use crate::link::NostrLink;
use crate::route::RouteServices;
-use crate::stream_info::StreamInfo;
use crate::theme::{MARGIN_DEFAULT, NEUTRAL_800, ROUNDING_DEFAULT};
use crate::widgets::{
sub_or_poll, Chat, NostrWidget, PlaceholderRect, StreamPlayer, StreamTitle, WriteChat,
};
-use egui::{vec2, Align, Frame, Layout, Response, Stroke, Ui, Vec2, Widget};
+use egui::{vec2, Align, Frame, Layout, Response, ScrollArea, Stroke, Ui, Vec2, Widget};
use nostrdb::{Filter, Note};
use crate::note_ref::NoteRef;
+use crate::stream_info::StreamInfo;
use crate::sub::SubRef;
use std::borrow::Borrow;
use std::collections::HashSet;
@@ -95,49 +95,51 @@ impl StreamPage {
let video_width = ui.available_width() - chat_w;
let video_height = max_h.min((video_width / 16.0) * 9.0);
- ui.with_layout(
- Layout::left_to_right(Align::TOP).with_main_justify(true),
- |ui| {
- ui.vertical(|ui| {
- ui.allocate_ui(vec2(video_width, video_height), |ui| {
+ ui.horizontal(|ui| {
+ ui.allocate_ui_with_layout(
+ vec2(video_width, max_h),
+ Layout::top_down_justified(Align::Min),
+ |ui| {
+ ScrollArea::vertical().show(ui, |ui| {
if let Some(player) = &mut self.player {
- player.ui(ui)
+ ui.add_sized(vec2(video_width, video_height), player);
} else {
- ui.add(PlaceholderRect)
+ ui.add_sized(vec2(video_width, video_height), PlaceholderRect);
}
+
+ ui.add_space(10.);
+ StreamTitle::new(event).render(ui, services);
});
- ui.add_space(10.);
- StreamTitle::new(event).render(ui, services);
- });
- ui.allocate_ui_with_layout(
- vec2(chat_w, max_h),
- Layout::top_down_justified(Align::Min),
- |ui| {
- Frame::none()
- .stroke(Stroke::new(1.0, NEUTRAL_800))
- .outer_margin(MARGIN_DEFAULT)
- .rounding(ROUNDING_DEFAULT)
- .show(ui, |ui| {
- let chat_h = 60.0;
- if let Some(c) = self.chat.as_mut() {
- ui.allocate_ui(
- vec2(ui.available_width(), ui.available_height() - chat_h),
- |ui| {
- c.render(ui, services);
- },
- );
- } else {
- ui.label("Loading..");
- }
- if ui.available_height().is_finite() {
- ui.add_space(ui.available_height() - chat_h);
- }
- self.new_msg.render(ui, services);
- });
- },
- );
- },
- );
+ },
+ );
+ ui.allocate_ui_with_layout(
+ vec2(chat_w, max_h),
+ Layout::top_down_justified(Align::Min),
+ |ui| {
+ Frame::none()
+ .stroke(Stroke::new(1.0, NEUTRAL_800))
+ .outer_margin(MARGIN_DEFAULT)
+ .rounding(ROUNDING_DEFAULT)
+ .show(ui, |ui| {
+ let chat_h = 60.0;
+ if let Some(c) = self.chat.as_mut() {
+ ui.allocate_ui(
+ vec2(ui.available_width(), ui.available_height() - chat_h),
+ |ui| {
+ c.render(ui, services);
+ },
+ );
+ } else {
+ ui.label("Loading..");
+ }
+ if ui.available_height().is_finite() {
+ ui.add_space(ui.available_height() - chat_h);
+ }
+ self.new_msg.render(ui, services);
+ });
+ },
+ );
+ });
ui.response()
}
@@ -152,11 +154,16 @@ impl NostrWidget for StreamPage {
.collect();
if let Some(event) = events.first() {
- if let Some(stream) = event.streaming() {
- if self.player.is_none() {
- let p = StreamPlayer::new(ui.ctx(), &stream.to_string());
- self.player = Some(p);
- }
+ if self.player.is_none() {
+ match event.kind() {
+ 30_311 => {
+ if let Some(u) = event.streaming().or(event.recording()) {
+ let p = StreamPlayer::new(ui.ctx(), &u.to_string());
+ self.player = Some(p);
+ }
+ }
+ _ => {}
+ };
}
if self.chat.is_none() {
diff --git a/src/services/ffmpeg_loader.rs b/src/services/ffmpeg_loader.rs
index 2e49384..54e51b6 100644
--- a/src/services/ffmpeg_loader.rs
+++ b/src/services/ffmpeg_loader.rs
@@ -1,5 +1,5 @@
use anyhow::Error;
-use egui::ColorImage;
+use egui::{ColorImage, Vec2};
use egui_video::ffmpeg_rs_raw::{get_frame_from_hw, Decoder, Demuxer, Scaler};
use egui_video::ffmpeg_sys_the_third::{av_frame_free, av_packet_free, AVPixelFormat};
use egui_video::media_player::video_frame_to_image;
@@ -12,17 +12,25 @@ impl FfmpegLoader {
Self {}
}
- pub fn load_image(&self, path: PathBuf) -> Result {
+ pub fn load_image(&self, path: PathBuf, size: Option) -> Result {
let demux = Demuxer::new(path.to_str().unwrap())?;
- Self::load_image_from_demuxer(demux)
+ Self::load_image_from_demuxer(demux, size)
}
- pub fn load_image_bytes(&self, key: &str, data: &'static [u8]) -> Result {
+ pub fn load_image_bytes(
+ &self,
+ key: &str,
+ data: &'static [u8],
+ size: Option,
+ ) -> Result {
let demux = Demuxer::new_custom_io(data, Some(key.to_string()))?;
- Self::load_image_from_demuxer(demux)
+ Self::load_image_from_demuxer(demux, size)
}
- fn load_image_from_demuxer(mut demuxer: Demuxer) -> Result {
+ fn load_image_from_demuxer(
+ mut demuxer: Demuxer,
+ size: Option,
+ ) -> Result {
unsafe {
let info = demuxer.probe_input()?;
@@ -46,8 +54,8 @@ impl FfmpegLoader {
let mut frame = get_frame_from_hw(*frame)?;
let frame_rgb = scaler.process_frame(
frame,
- (*frame).width as u16,
- (*frame).height as u16,
+ size.map(|s| s.x as u16).unwrap_or((*frame).width as u16),
+ size.map(|s| s.y as u16).unwrap_or((*frame).height as u16),
rgb,
)?;
av_frame_free(&mut frame);
diff --git a/src/stream_info.rs b/src/stream_info.rs
index afd8d80..f6a966e 100644
--- a/src/stream_info.rs
+++ b/src/stream_info.rs
@@ -89,6 +89,9 @@ impl StreamInfo for Note<'_> {
/// Is the stream playable by this app
fn can_play(&self) -> bool {
+ if self.kind() == 30_313 {
+ return true; // n94-stream can always be played
+ }
if let Some(stream) = self.streaming() {
stream.contains(".m3u8")
} else {
diff --git a/src/widgets/avatar.rs b/src/widgets/avatar.rs
index f22616c..6b5cd4c 100644
--- a/src/widgets/avatar.rs
+++ b/src/widgets/avatar.rs
@@ -1,5 +1,6 @@
use crate::route::image_from_cache;
-use egui::{vec2, Color32, Pos2, Response, Rounding, Sense, Ui, Vec2, Widget};
+use crate::theme::NEUTRAL_800;
+use egui::{vec2, Response, Rounding, Sense, Ui, Vec2, Widget};
use nostrdb::{Ndb, NdbProfile, Transaction};
use notedeck::ImageCache;
@@ -42,11 +43,8 @@ impl Avatar {
fn placeholder(ui: &mut Ui, size: f32) -> Response {
let (response, painter) = ui.allocate_painter(vec2(size, size), Sense::click());
- painter.circle_filled(
- Pos2::new(size / 2., size / 2.),
- size / 2.,
- Color32::from_rgb(200, 200, 200),
- );
+ let pos = response.rect.min + vec2(size / 2., size / 2.);
+ painter.circle_filled(pos, size / 2., NEUTRAL_800);
response
}
@@ -57,9 +55,7 @@ impl Avatar {
return Self::placeholder(ui, size_v);
}
match &self.image {
- Some(img) => image_from_cache(img_cache, ui, img)
- .max_size(size)
- .fit_to_exact_size(size)
+ Some(img) => image_from_cache(img_cache, ui, img, Some(size))
.rounding(Rounding::same(size_v))
.sense(Sense::click())
.ui(ui),
diff --git a/src/widgets/chat.rs b/src/widgets/chat.rs
index 7eda016..f2d13d8 100644
--- a/src/widgets/chat.rs
+++ b/src/widgets/chat.rs
@@ -64,7 +64,7 @@ impl NostrWidget for Chat {
1311 => {
let profile = services.profile(ev.pubkey());
ChatMessage::new(&stream, &ev, &profile)
- .render(ui, services.ctx.img_cache);
+ .render(ui, services);
}
9735 => {
if let Ok(zap) = Zap::from_receipt(ev) {
diff --git a/src/widgets/chat_message.rs b/src/widgets/chat_message.rs
index 3b7bb60..13c5ce3 100644
--- a/src/widgets/chat_message.rs
+++ b/src/widgets/chat_message.rs
@@ -1,3 +1,5 @@
+use crate::link::NostrLink;
+use crate::route::{RouteServices, RouteType};
use crate::stream_info::StreamInfo;
use crate::theme::{NEUTRAL_500, PRIMARY};
use crate::widgets::Avatar;
@@ -5,7 +7,6 @@ use eframe::epaint::text::TextWrapMode;
use egui::text::LayoutJob;
use egui::{Align, Color32, Label, Response, TextFormat, Ui};
use nostrdb::{NdbProfile, Note};
-use notedeck::ImageCache;
pub struct ChatMessage<'a> {
stream: &'a Note<'a>,
@@ -26,7 +27,7 @@ impl<'a> ChatMessage<'a> {
}
}
- pub fn render(self, ui: &mut Ui, img_cache: &mut ImageCache) -> Response {
+ pub fn render(self, ui: &mut Ui, services: &mut RouteServices) -> Response {
ui.horizontal_wrapped(|ui| {
let mut job = LayoutJob::default();
// TODO: avoid this somehow
@@ -48,9 +49,15 @@ impl<'a> ChatMessage<'a> {
format.color = Color32::WHITE;
job.append(self.ev.content(), 5.0, format.clone());
- Avatar::from_profile(self.profile)
+ if Avatar::from_profile(self.profile)
.size(24.)
- .render(ui, img_cache);
+ .render(ui, services.ctx.img_cache)
+ .clicked()
+ {
+ services.navigate(RouteType::ProfilePage {
+ link: NostrLink::profile(self.ev.pubkey()),
+ })
+ }
ui.add(Label::new(job).wrap_mode(TextWrapMode::Wrap));
// consume reset of space
diff --git a/src/widgets/stream_tile.rs b/src/widgets/stream_tile.rs
index b7c6658..c65dc94 100644
--- a/src/widgets/stream_tile.rs
+++ b/src/widgets/stream_tile.rs
@@ -5,10 +5,9 @@ use crate::theme::{NEUTRAL_800, NEUTRAL_900, PRIMARY, ROUNDING_DEFAULT};
use crate::widgets::avatar::Avatar;
use eframe::epaint::{Rounding, Vec2};
use egui::epaint::RectShape;
-use egui::load::TexturePoll;
use egui::{
- vec2, Color32, CursorIcon, FontId, Label, Pos2, Rect, Response, RichText, Sense, TextWrapMode,
- Ui,
+ vec2, Color32, CursorIcon, FontId, ImageSource, Label, Pos2, Rect, Response, RichText, Sense,
+ TextWrapMode, Ui,
};
use nostrdb::Note;
@@ -34,33 +33,27 @@ impl<'a> StreamEvent<'a> {
let (response, painter) = ui.allocate_painter(Vec2::new(w, h), Sense::click());
let cover = if ui.is_rect_visible(response.rect) {
- self.event
- .image()
- .map(|p| image_from_cache(services.ctx.img_cache, ui, p))
+ self.event.image().map(|p| {
+ image_from_cache(services.ctx.img_cache, ui, p, Some(Vec2::new(w, h)))
+ .rounding(ROUNDING_DEFAULT)
+ })
} else {
None
};
- if let Some(cover) = cover.map(|c| {
- c.rounding(Rounding::same(12.))
- .load_for_size(painter.ctx(), Vec2::new(w, h))
- }) {
- match cover {
- Ok(TexturePoll::Ready { texture }) => {
- painter.add(RectShape {
- rect: response.rect,
- rounding: Rounding::same(ROUNDING_DEFAULT),
- fill: Color32::WHITE,
- stroke: Default::default(),
- blur_width: 0.0,
- fill_texture_id: texture.id,
- uv: Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(1.0, 1.0)),
- });
- }
- _ => {
- painter.rect_filled(response.rect, ROUNDING_DEFAULT, NEUTRAL_800);
- }
- }
+ if let Some(cover) = cover {
+ painter.add(RectShape {
+ rect: response.rect,
+ rounding: Rounding::same(ROUNDING_DEFAULT),
+ fill: Color32::WHITE,
+ stroke: Default::default(),
+ blur_width: 0.0,
+ fill_texture_id: match cover.source(ui.ctx()) {
+ ImageSource::Texture(t) => t.id,
+ _ => Default::default(),
+ },
+ uv: Rect::from_min_max(Pos2::new(0.0, 0.0), Pos2::new(1.0, 1.0)),
+ });
} else {
painter.rect_filled(response.rect, ROUNDING_DEFAULT, NEUTRAL_800);
}