Skip to content

Commit 654d779

Browse files
committed
fix: correction on rebase for last master
1 parent 31e0c3d commit 654d779

File tree

19 files changed

+322
-765
lines changed

19 files changed

+322
-765
lines changed

Cargo.lock

Lines changed: 81 additions & 487 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/config/common.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,7 @@ impl CommonConfig {
336336
let revealer = revealer.clone();
337337
let container = container.clone();
338338

339-
show_if.subscribe((revealer, &container), |(revealer, container), success| {
340-
if success {
341-
container.show();
342-
}
339+
show_if.subscribe(&revealer, |revealer, success| {
343340
revealer.set_reveal_child(success);
344341
});
345342
}

src/image/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ mod provider;
1616
feature = "workspaces",
1717
))]
1818
pub use self::gtk::*;
19-
pub use provider::{Provider, create_and_load_surface};
19+
pub use provider::Provider;

src/image/provider.rs

Lines changed: 128 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
use crate::desktop_file::DesktopFiles;
22
use crate::{arc_mut, lock};
3+
use cfg_if::cfg_if;
34
use color_eyre::{Help, Report, Result};
45
use gtk::cairo::Surface;
56
use gtk::gdk_pixbuf::Pixbuf;
6-
use gtk::gio::{Cancellable, MemoryInputStream};
77
use gtk::prelude::*;
88
use gtk::{IconLookupFlags, IconTheme, Image};
99
use std::cell::RefCell;
1010
use std::collections::HashMap;
11-
use std::path::PathBuf;
1211
use std::path::{Path, PathBuf};
1312
use std::sync::{Arc, Mutex};
1413
#[cfg(feature = "http")]
1514
use tokio::sync::mpsc;
1615
use tracing::{debug, trace, warn};
17-
use tracing::{debug, warn};
16+
17+
cfg_if!(
18+
if #[cfg(feature = "http")] {
19+
use gtk::gio::{Cancellable, MemoryInputStream};
20+
use tracing::error;
21+
}
22+
);
1823

1924
#[derive(Debug, Clone, Eq, Hash, PartialEq)]
2025
struct ImageRef {
@@ -78,44 +83,6 @@ impl Provider {
7883
}
7984
}
8085

81-
/// Attempts to resolve the provided input into a `Pixbuf`,
82-
/// and load that `Pixbuf` into the provided `Image` widget.
83-
///
84-
/// If `use_fallback` is `true`, a fallback icon will be used
85-
/// where an image cannot be found.
86-
///
87-
/// Returns `true` if the image was successfully loaded,
88-
/// or `false` if the image could not be found.
89-
/// May also return an error if the resolution or loading process failed.
90-
pub async fn load_into_image(
91-
&self,
92-
input: &str,
93-
size: i32,
94-
use_fallback: bool,
95-
image: &Image,
96-
) -> Result<bool> {
97-
let image_ref = self.get_ref(input, size).await?;
98-
debug!("image ref for {input}: {:?}", image_ref);
99-
100-
let pixbuf = if let Some(pixbuf) = lock!(self.cache).pixbuf_cache.get(&image_ref) {
101-
pixbuf.clone()
102-
} else {
103-
let pixbuf = Self::get_pixbuf(&image_ref, image.scale_factor(), use_fallback).await?;
104-
105-
lock!(self.cache)
106-
.pixbuf_cache
107-
.insert(image_ref, pixbuf.clone());
108-
109-
pixbuf
110-
};
111-
112-
if let Some(ref pixbuf) = pixbuf {
113-
create_and_load_surface(pixbuf, image)?;
114-
}
115-
116-
Ok(pixbuf.is_some())
117-
}
118-
11986
/// Like [`Provider::load_into_image`], but does not return an error if the image could not be found.
12087
///
12188
/// If an image is not resolved, a warning is logged. Errors are also logged.
@@ -137,13 +104,13 @@ impl Provider {
137104
///
138105
/// This contains the location of the image if it can be resolved.
139106
/// The ref will be loaded from cache if present.
140-
async fn get_ref(&self, input: &str, size: i32) -> Result<ImageRef> {
107+
async fn get_ref(&self, input: &str, use_fallback: bool, size: i32) -> Result<ImageRef> {
141108
let key = (input.into(), size);
142109

143110
if let Some(location) = lock!(self.cache).location_cache.get(&key) {
144111
Ok(location.clone())
145112
} else {
146-
let location = self.resolve_location(input, size, 0).await?;
113+
let location = self.resolve_location(input, size, use_fallback, 0).await?;
147114
let image_ref = ImageRef::new(size, location, self.icon_theme());
148115

149116
lock!(self.cache)
@@ -153,6 +120,17 @@ impl Provider {
153120
}
154121
}
155122

123+
/// Returns true if the input starts with a prefix
124+
/// that is supported by the parser
125+
/// (i.e. the parser would not fall back to checking the input).
126+
pub fn is_explicit_input(input: &str) -> bool {
127+
input.starts_with("icon:")
128+
|| input.starts_with("file://")
129+
|| input.starts_with("http://")
130+
|| input.starts_with("https://")
131+
|| input.starts_with('/')
132+
}
133+
156134
/// Attempts to resolve the provided input into an `ImageLocation`.
157135
///
158136
/// This will resolve all of:
@@ -165,8 +143,19 @@ impl Provider {
165143
&self,
166144
input: &str,
167145
size: i32,
146+
use_fallback: bool,
168147
recurse_depth: u8,
169148
) -> Result<Option<ImageLocation>> {
149+
macro_rules! fallback {
150+
() => {
151+
if use_fallback {
152+
Some(Self::get_fallback_icon())
153+
} else {
154+
None
155+
}
156+
};
157+
}
158+
170159
const MAX_RECURSE_DEPTH: u8 = 2;
171160

172161
let input = self.overrides.get(input).map_or(input, String::as_str);
@@ -188,9 +177,11 @@ impl Provider {
188177
input_name.chars().skip("steam_app_".len()).collect(),
189178
)),
190179
None if self
191-
.icon_theme()
192-
.lookup_icon(input_name, size, IconLookupFlags::empty())
193-
.is_some() =>
180+
.icon_theme
181+
.borrow()
182+
.as_ref()
183+
.map(|t| t.has_icon(input))
184+
.unwrap_or(false) =>
194185
{
195186
Some(ImageLocation::Icon(input_name.to_string()))
196187
}
@@ -200,7 +191,7 @@ impl Provider {
200191
Report::msg(format!("Unsupported image type: {input_type}"))
201192
.note("You may need to recompile with support if available")
202193
);
203-
None
194+
fallback!()
204195
}
205196
None if PathBuf::from(input_name).is_file() => {
206197
Some(ImageLocation::Local(PathBuf::from(input_name)))
@@ -217,96 +208,104 @@ impl Provider {
217208
if location == input_name {
218209
None
219210
} else {
220-
Box::pin(self.resolve_location(&location, size, recurse_depth + 1)).await?
211+
Box::pin(self.resolve_location(
212+
&location,
213+
size,
214+
use_fallback,
215+
recurse_depth + 1,
216+
))
217+
.await?
221218
}
222219
} else {
223-
None
220+
warn!("Failed to find image: {input}");
221+
fallback!()
224222
}
225223
}
226-
None => None,
224+
None => {
225+
warn!("Failed to find image: {input}");
226+
fallback!()
227+
}
227228
};
228229

229230
Ok(location)
230231
}
231232

232-
/// Attempts to load the provided `ImageRef` into a `Pixbuf`.
233-
///
234-
/// If `use_fallback` is `true`, a fallback icon will be used
235-
/// where an image cannot be found.
236-
async fn get_pixbuf(
237-
image_ref: &ImageRef,
238-
scale: i32,
233+
/// Attempts to fetch the image from the location
234+
/// and load it into the provided `GTK::Image` widget.
235+
pub async fn load_into_image(
236+
&self,
237+
input: &str,
238+
size: i32,
239239
use_fallback: bool,
240-
) -> Result<Option<Pixbuf>> {
241-
const FALLBACK_ICON_NAME: &str = "dialog-question-symbolic";
242-
243-
let buf = match &image_ref.location {
244-
Some(ImageLocation::Icon(name)) => image_ref.theme.load_icon_for_scale(
245-
name,
246-
image_ref.size,
247-
scale,
248-
IconLookupFlags::FORCE_SIZE,
249-
),
250-
Some(ImageLocation::Local(path)) => {
251-
let scaled_size = image_ref.size * scale;
252-
Pixbuf::from_file_at_scale(path, scaled_size, scaled_size, true).map(Some)
253-
}
254-
Some(ImageLocation::Steam(app_id)) => {
255-
let path = dirs::data_dir().map_or_else(
256-
|| Err(Report::msg("Missing XDG data dir")),
257-
|dir| Ok(dir.join(format!("icons/hicolor/32x32/apps/steam_icon_{app_id}.png"))),
258-
)?;
259-
260-
let scaled_size = image_ref.size * scale;
261-
Pixbuf::from_file_at_scale(path, scaled_size, scaled_size, true).map(Some)
262-
}
263-
#[cfg(feature = "http")]
264-
Some(ImageLocation::Remote(uri)) => {
265-
let res = reqwest::get(uri.clone()).await?;
240+
image: &Image,
241+
) -> Result<bool> {
242+
let image_ref = self.get_ref(input, use_fallback, size).await?;
243+
let scale = image.scale_factor();
244+
// handle remote locations async to avoid blocking UI thread while downloading
245+
#[cfg(feature = "http")]
246+
if let Some(ImageLocation::Remote(url)) = &image_ref.location {
247+
let res = reqwest::get(url.clone()).await?;
248+
249+
let status = res.status();
250+
let bytes = if status.is_success() {
251+
let bytes = res.bytes().await?;
252+
Ok(glib::Bytes::from_owned(bytes))
253+
} else {
254+
Err(Report::msg(format!(
255+
"Received non-success HTTP code ({status})"
256+
)))
257+
}?;
258+
259+
let stream = MemoryInputStream::from_bytes(&bytes);
260+
let scaled_size = image_ref.size * scale;
261+
262+
let pixbuf = Pixbuf::from_stream_at_scale(
263+
&stream,
264+
scaled_size,
265+
scaled_size,
266+
true,
267+
Some(&Cancellable::new()),
268+
)
269+
.map(Some)?;
270+
271+
image.set_from_pixbuf(pixbuf.as_ref());
272+
} else {
273+
self.load_into_image_sync(&image_ref, image);
274+
};
266275

267-
let status = res.status();
268-
let bytes = if status.is_success() {
269-
let bytes = res.bytes().await?;
270-
Ok(glib::Bytes::from_owned(bytes))
271-
} else {
272-
Err(Report::msg(format!(
273-
"Received non-success HTTP code ({status})"
274-
)))
275-
}?;
276-
277-
let stream = MemoryInputStream::from_bytes(&bytes);
278-
let scaled_size = image_ref.size * scale;
279-
280-
Pixbuf::from_stream_at_scale(
281-
&stream,
282-
scaled_size,
283-
scaled_size,
284-
true,
285-
Some(&Cancellable::new()),
286-
)
287-
.map(Some)
288-
}
289-
None if use_fallback => image_ref.theme.load_icon_for_scale(
290-
FALLBACK_ICON_NAME,
291-
image_ref.size,
292-
scale,
293-
IconLookupFlags::empty(),
294-
),
295-
None => Ok(None),
296-
}?;
297-
298-
Ok(buf)
276+
#[cfg(not(feature = "http"))]
277+
self.load_into_image_sync(&image_ref, image);
278+
279+
Ok(true)
299280
}
300281

301-
/// Returns true if the input starts with a prefix
302-
/// that is supported by the parser
303-
/// (i.e. the parser would not fall back to checking the input).
304-
pub fn is_explicit_input(input: &str) -> bool {
305-
input.starts_with("icon:")
306-
|| input.starts_with("file://")
307-
|| input.starts_with("http://")
308-
|| input.starts_with("https://")
309-
|| input.starts_with('/')
282+
/// Attempts to synchronously fetch an image from location
283+
/// and load into into the image.
284+
fn load_into_image_sync(&self, image_ref: &ImageRef, image: &Image) {
285+
let scale = image.scale_factor();
286+
287+
if let Some(location) = &image_ref.location {
288+
match location {
289+
ImageLocation::Icon(name) => image.set_icon_name(Some(name)),
290+
ImageLocation::Local(path) => image.set_from_file(Some(path)),
291+
ImageLocation::Steam(steam_id) => {
292+
image.set_from_file(Self::steam_id_to_path(steam_id).ok())
293+
}
294+
#[cfg(feature = "http")]
295+
_ => unreachable!(), // handled above
296+
};
297+
}
298+
}
299+
300+
fn steam_id_to_path(steam_id: &str) -> Result<PathBuf> {
301+
dirs::data_dir().map_or_else(
302+
|| Err(Report::msg("Missing XDG data dir")),
303+
|dir| {
304+
Ok(dir.join(format!(
305+
"icons/hicolor/32x32/apps/steam_icon_{steam_id}.png"
306+
)))
307+
},
308+
)
310309
}
311310

312311
pub fn icon_theme(&self) -> IconTheme {
@@ -323,31 +322,14 @@ impl Provider {
323322

324323
*self.icon_theme.borrow_mut() = if theme.is_some() {
325324
let icon_theme = IconTheme::new();
326-
icon_theme.set_custom_theme(theme);
325+
icon_theme.set_theme_name(theme);
327326
Some(icon_theme)
328327
} else {
329-
IconTheme::default()
328+
Some(IconTheme::default())
330329
};
331330
}
332-
}
333331

334-
/// Attempts to create a Cairo `Surface` from the provided `Pixbuf`,
335-
/// using the provided scaling factor.
336-
/// The surface is then loaded into the provided image.
337-
///
338-
/// This is necessary for HiDPI since `Pixbuf`s are always treated as scale factor 1.
339-
pub fn create_and_load_surface(pixbuf: &Pixbuf, image: &Image) -> Result<()> {
340-
let surface = unsafe {
341-
let ptr = gdk_cairo_surface_create_from_pixbuf(
342-
pixbuf.as_ptr(),
343-
image.scale_factor(),
344-
std::ptr::null_mut(),
345-
);
346-
347-
Surface::from_raw_full(ptr)
348-
}?;
349-
350-
image.set_from_surface(Some(&surface));
351-
352-
Ok(())
332+
fn get_fallback_icon() -> ImageLocation {
333+
ImageLocation::Icon("dialog-question-symbolic".to_string())
334+
}
353335
}

0 commit comments

Comments
 (0)