mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
chore: use if state for hovered icon
This commit is contained in:
parent
ec730960ac
commit
d933c0e6f1
6 changed files with 107 additions and 65 deletions
|
|
@ -5,11 +5,9 @@ use mlua::{UserData, UserDataFields, Value};
|
||||||
use crate::{Style, cached_field};
|
use crate::{Style, cached_field};
|
||||||
|
|
||||||
pub struct Icon {
|
pub struct Icon {
|
||||||
inner: &'static yazi_config::Icon,
|
inner: &'static yazi_config::Icon,
|
||||||
|
v_text: Option<Value>,
|
||||||
v_text: Option<Value>,
|
v_style: Option<Value>,
|
||||||
v_style: Option<Value>,
|
|
||||||
v_hovered_text: Option<Value>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Icon {
|
impl Deref for Icon {
|
||||||
|
|
@ -20,7 +18,7 @@ impl Deref for Icon {
|
||||||
|
|
||||||
impl From<&'static yazi_config::Icon> for Icon {
|
impl From<&'static yazi_config::Icon> for Icon {
|
||||||
fn from(icon: &'static yazi_config::Icon) -> Self {
|
fn from(icon: &'static yazi_config::Icon) -> Self {
|
||||||
Self { inner: icon, v_text: None, v_style: None, v_hovered_text: None }
|
Self { inner: icon, v_text: None, v_style: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,8 +26,5 @@ impl UserData for Icon {
|
||||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||||
cached_field!(fields, text, |lua, me| lua.create_string(&me.text));
|
cached_field!(fields, text, |lua, me| lua.create_string(&me.text));
|
||||||
cached_field!(fields, style, |_, me| Ok(Style::from(me.style)));
|
cached_field!(fields, style, |_, me| Ok(Style::from(me.style)));
|
||||||
cached_field!(fields, hovered_text, |lua, me| {
|
|
||||||
me.hovered_text.as_ref().map(|s| lua.create_string(s)).transpose()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -268,10 +268,10 @@ macro_rules! impl_file_methods {
|
||||||
Ok(me.hash_u64())
|
Ok(me.hash_u64())
|
||||||
});
|
});
|
||||||
|
|
||||||
$methods.add_method("icon", |_, me, ()| {
|
$methods.add_method("icon", |_, me, hovered: Option<bool>| {
|
||||||
use $crate::Icon;
|
use $crate::Icon;
|
||||||
// TODO: use a cache
|
// TODO: use a cache
|
||||||
Ok(yazi_config::THEME.icon.matches(me).map(Icon::from))
|
Ok(yazi_config::THEME.icon.matches(me, hovered.unwrap_or(false)).map(Icon::from))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -997,7 +997,8 @@ conds = [
|
||||||
{ if = "dummy", text = "", fg = "#f44336" },
|
{ if = "dummy", text = "", fg = "#f44336" },
|
||||||
|
|
||||||
# Fallback
|
# Fallback
|
||||||
{ if = "dir", text = "", fg = "#03a9f4", hovered_text = "" },
|
{ if = "dir & hovered", text = "" },
|
||||||
|
{ if = "dir", text = "", fg = "#03a9f4" },
|
||||||
{ if = "exec", text = "", fg = "#8bc34a" },
|
{ if = "exec", text = "", fg = "#8bc34a" },
|
||||||
{ if = "!dir", text = "", fg = "#ffffff" },
|
{ if = "!dir", text = "", fg = "#ffffff" },
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ use crate::Style;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Icon {
|
pub struct Icon {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub style: Style,
|
pub style: Style,
|
||||||
pub hovered_text: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ pub struct Icon {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Icon {
|
impl Icon {
|
||||||
pub fn matches(&self, file: &File) -> Option<&I> {
|
pub fn matches(&self, file: &File, hovered: bool) -> Option<&I> {
|
||||||
if let Some(i) = self.match_by_glob(file) {
|
if let Some(i) = self.match_by_glob(file) {
|
||||||
return Some(i);
|
return Some(i);
|
||||||
}
|
}
|
||||||
|
|
@ -65,6 +65,7 @@ impl Icon {
|
||||||
"sock" => file.is_sock(),
|
"sock" => file.is_sock(),
|
||||||
"exec" => file.is_exec(),
|
"exec" => file.is_exec(),
|
||||||
"sticky" => file.is_sticky(),
|
"sticky" => file.is_sticky(),
|
||||||
|
"hovered" => hovered,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
self.conds.iter().find(|(c, _)| c.eval(f) == Some(true)).map(|(_, i)| i)
|
self.conds.iter().find(|(c, _)| c.eval(f) == Some(true)).map(|(_, i)| i)
|
||||||
|
|
@ -132,22 +133,15 @@ impl<'de> Deserialize<'de> for PatIcons {
|
||||||
{
|
{
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Shadow {
|
struct Shadow {
|
||||||
url: Pattern,
|
url: Pattern,
|
||||||
text: String,
|
text: String,
|
||||||
fg: Option<Color>,
|
fg: Option<Color>,
|
||||||
hovered_text: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self(
|
Ok(Self(
|
||||||
<Vec<Shadow>>::deserialize(deserializer)?
|
<Vec<Shadow>>::deserialize(deserializer)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| {
|
.map(|s| (s.url, I { text: s.text, style: Style { fg: s.fg, ..Default::default() } }))
|
||||||
(s.url, I {
|
|
||||||
text: s.text,
|
|
||||||
style: Style { fg: s.fg, ..Default::default() },
|
|
||||||
hovered_text: s.hovered_text,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
@ -169,22 +163,15 @@ impl<'de> Deserialize<'de> for StrIcons {
|
||||||
{
|
{
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Shadow {
|
struct Shadow {
|
||||||
name: String,
|
name: String,
|
||||||
text: String,
|
text: String,
|
||||||
fg: Option<Color>,
|
fg: Option<Color>,
|
||||||
hovered_text: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self(
|
Ok(Self(
|
||||||
<Vec<Shadow>>::deserialize(deserializer)?
|
<Vec<Shadow>>::deserialize(deserializer)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| {
|
.map(|s| (s.name, I { text: s.text, style: Style { fg: s.fg, ..Default::default() } }))
|
||||||
(s.name, I {
|
|
||||||
text: s.text,
|
|
||||||
style: Style { fg: s.fg, ..Default::default() },
|
|
||||||
hovered_text: s.hovered_text,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
@ -206,22 +193,15 @@ impl<'de> Deserialize<'de> for CondIcons {
|
||||||
{
|
{
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Shadow {
|
struct Shadow {
|
||||||
r#if: Condition,
|
r#if: Condition,
|
||||||
text: String,
|
text: String,
|
||||||
fg: Option<Color>,
|
fg: Option<Color>,
|
||||||
hovered_text: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self(
|
Ok(Self(
|
||||||
<Vec<Shadow>>::deserialize(deserializer)?
|
<Vec<Shadow>>::deserialize(deserializer)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| {
|
.map(|s| (s.r#if, I { text: s.text, style: Style { fg: s.fg, ..Default::default() } }))
|
||||||
(s.r#if, I {
|
|
||||||
text: s.text,
|
|
||||||
style: Style { fg: s.fg, ..Default::default() },
|
|
||||||
hovered_text: s.hovered_text,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
@ -229,24 +209,94 @@ impl<'de> Deserialize<'de> for CondIcons {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use yazi_fs::{File, cha::{Cha, ChaType}};
|
||||||
|
use yazi_shared::url::Url;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
fn create_test_file(name: &str, cha_type: ChaType) -> File {
|
||||||
|
let url = Url::regular(name).to_owned();
|
||||||
|
File::from_dummy(url, Some(cha_type))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_icon_struct_with_hovered_text() {
|
fn test_matches_hovered() {
|
||||||
let icon = I {
|
// icon with hovered only
|
||||||
text: "normal_icon".to_string(),
|
let icon = Icon {
|
||||||
style: Style::default(),
|
conds: CondIcons(vec![(Condition::from_str("hovered").unwrap(), I {
|
||||||
hovered_text: Some("hovered_icon".to_string()),
|
text: "hovered_icon".to_string(),
|
||||||
|
style: Style::default(),
|
||||||
|
})]),
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(icon.text, "normal_icon");
|
let file = create_test_file("test.txt", ChaType::File);
|
||||||
assert_eq!(icon.hovered_text, Some("hovered_icon".to_string()));
|
|
||||||
|
// should match when hovered
|
||||||
|
let result = icon.matches(&file, true);
|
||||||
|
assert!(result.is_some());
|
||||||
|
assert_eq!(result.unwrap().text, "hovered_icon");
|
||||||
|
|
||||||
|
// should not match when not hovered
|
||||||
|
let result = icon.matches(&file, false);
|
||||||
|
assert!(result.is_none());
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_icon_struct_without_hovered_text() {
|
fn test_matches_dir_and_hovered_condition() {
|
||||||
let icon =
|
// icon with dir and hovered
|
||||||
I { text: "normal_icon".to_string(), style: Style::default(), hovered_text: None };
|
let icon = Icon {
|
||||||
|
conds: CondIcons(vec![(Condition::from_str("dir & hovered").unwrap(), I {
|
||||||
|
text: "dir_hovered".to_string(),
|
||||||
|
style: Style::default(),
|
||||||
|
})]),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
assert_eq!(icon.text, "normal_icon");
|
let dir_file = create_test_file("test_dir", ChaType::Dir);
|
||||||
assert_eq!(icon.hovered_text, None);
|
let file = create_test_file("test.txt", ChaType::File);
|
||||||
|
|
||||||
|
// directory + hovered
|
||||||
|
let result = icon.matches(&dir_file, true);
|
||||||
|
assert!(result.is_some());
|
||||||
|
assert_eq!(result.unwrap().text, "dir_hovered");
|
||||||
|
|
||||||
|
// directory + not hovered
|
||||||
|
let result = icon.matches(&dir_file, false);
|
||||||
|
assert!(result.is_none());
|
||||||
|
|
||||||
|
// file + hovered isnt present
|
||||||
|
let result = icon.matches(&file, true);
|
||||||
|
assert!(result.is_none());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_matches_hovered_before_dir() {
|
||||||
|
// icon with hovered before dir
|
||||||
|
let icon = Icon {
|
||||||
|
conds: CondIcons(vec![
|
||||||
|
(Condition::from_str("dir & hovered").unwrap(), I {
|
||||||
|
text: "dir_hovered".to_string(),
|
||||||
|
style: Style::default(),
|
||||||
|
}),
|
||||||
|
(Condition::from_str("dir").unwrap(), I {
|
||||||
|
text: "dir_normal".to_string(),
|
||||||
|
style: Style::default(),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let dir_file = create_test_file("test_dir", ChaType::Dir);
|
||||||
|
|
||||||
|
// hovered
|
||||||
|
let result = icon.matches(&dir_file, true);
|
||||||
|
assert!(result.is_some());
|
||||||
|
assert_eq!(result.unwrap().text, "dir_hovered");
|
||||||
|
|
||||||
|
// non-hovered
|
||||||
|
let result = icon.matches(&dir_file, false);
|
||||||
|
assert!(result.is_some());
|
||||||
|
assert_eq!(result.unwrap().text, "dir_normal");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,9 @@ function Entity:padding()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Entity:icon()
|
function Entity:icon()
|
||||||
local icon = self._file:icon()
|
local icon = self._file:icon(self._file.is_hovered)
|
||||||
if not icon then
|
if not icon then
|
||||||
return ""
|
return ""
|
||||||
elseif self._file.is_hovered then
|
|
||||||
local text = icon.hovered_text or icon.text
|
|
||||||
return text .. " "
|
|
||||||
else
|
else
|
||||||
return ui.Line(icon.text .. " "):style(icon.style)
|
return ui.Line(icon.text .. " "):style(icon.style)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue