mirror of
https://github.com/sxyazi/yazi.git
synced 2024-11-23 17:32:50 +03:00
feat!: decouple ui.List
, ui.Bar
, ui.Border
, and ui.Gauge
from coordinates (#1782)
This commit is contained in:
parent
43b5ae0e6c
commit
1acd7ca80f
@ -11,15 +11,15 @@ function Current:new(area, tab)
|
||||
end
|
||||
|
||||
function Current:empty()
|
||||
local line
|
||||
local text
|
||||
if self._folder.files.filter then
|
||||
line = ui.Line("No filter results")
|
||||
text = ui.Text("No filter results")
|
||||
else
|
||||
line = ui.Line(self._folder.stage.is_loading and "Loading..." or "No items")
|
||||
text = ui.Text(self._folder.stage.is_loading and "Loading..." or "No items")
|
||||
end
|
||||
|
||||
return {
|
||||
ui.Text(line):area(self._area):align(ui.Text.CENTER),
|
||||
text:area(self._area):align(ui.Text.CENTER),
|
||||
}
|
||||
end
|
||||
|
||||
@ -31,8 +31,8 @@ function Current:render()
|
||||
|
||||
local entities, linemodes = {}, {}
|
||||
for _, f in ipairs(files) do
|
||||
linemodes[#linemodes + 1] = Linemode:new(f):render()
|
||||
entities[#entities + 1] = Entity:new(f):render()
|
||||
linemodes[#linemodes + 1] = Linemode:new(f):render()
|
||||
end
|
||||
|
||||
return {
|
||||
|
@ -95,6 +95,7 @@ function Header:render()
|
||||
self._right_width = right:width()
|
||||
|
||||
local left = self:children_render(self.LEFT)
|
||||
|
||||
return {
|
||||
ui.Text(left):area(self._area),
|
||||
ui.Text(right):area(self._area):align(ui.Text.RIGHT),
|
||||
|
@ -29,7 +29,7 @@ function Marker:render()
|
||||
w = 1,
|
||||
h = math.min(1 + last[2] - last[1], self._area.y + self._area.h - y),
|
||||
}
|
||||
elements[#elements + 1] = ui.Bar(rect, ui.Bar.LEFT):style(last[3])
|
||||
elements[#elements + 1] = ui.Bar(ui.Bar.LEFT):area(rect):style(last[3])
|
||||
end
|
||||
|
||||
local last = { 0, 0, nil } -- start, end, style
|
||||
|
@ -21,7 +21,7 @@ function Progress:partial_render()
|
||||
return { ui.Text {} }
|
||||
end
|
||||
|
||||
local gauge = ui.Gauge(self._area)
|
||||
local gauge = ui.Gauge():area(self._area)
|
||||
if progress.fail == 0 then
|
||||
gauge = gauge:gauge_style(THEME.status.progress_normal)
|
||||
else
|
||||
|
@ -11,8 +11,8 @@ end
|
||||
|
||||
function Rail:build()
|
||||
self._base = {
|
||||
ui.Bar(self._chunks[1], ui.Bar.RIGHT):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style),
|
||||
ui.Bar(self._chunks[3], ui.Bar.LEFT):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style),
|
||||
ui.Bar(ui.Bar.RIGHT):area(self._chunks[1]):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style),
|
||||
ui.Bar(ui.Bar.LEFT):area(self._chunks[3]):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style),
|
||||
}
|
||||
self._children = {
|
||||
Marker:new(self._chunks[1], self._tab.parent),
|
||||
|
@ -132,8 +132,10 @@ end
|
||||
|
||||
function Status:render()
|
||||
local left = self:children_render(self.LEFT)
|
||||
|
||||
local right = self:children_render(self.RIGHT)
|
||||
local right_width = right:width()
|
||||
|
||||
return {
|
||||
ui.Text(left):area(self._area),
|
||||
ui.Text(right):area(self._area):align(ui.Text.RIGHT),
|
||||
|
@ -7,9 +7,7 @@ function M:peek()
|
||||
local files, bound, code = self.list_files({ "-p", tostring(self.file.url) }, self.skip, limit)
|
||||
if code ~= 0 then
|
||||
return ya.preview_widgets(self, {
|
||||
ui.Text(
|
||||
ui.Line(code == 2 and "File list in this archive is encrypted" or "Spawn `7z` and `7zz` both commands failed")
|
||||
)
|
||||
ui.Text(code == 2 and "File list in this archive is encrypted" or "Spawn `7z` and `7zz` both commands failed")
|
||||
:area(self.area),
|
||||
})
|
||||
end
|
||||
@ -27,9 +25,9 @@ function M:peek()
|
||||
end
|
||||
|
||||
if f.size > 0 then
|
||||
sizes[#sizes + 1] = ui.Line(string.format(" %s ", ya.readable_size(f.size)))
|
||||
sizes[#sizes + 1] = string.format(" %s ", ya.readable_size(f.size))
|
||||
else
|
||||
sizes[#sizes + 1] = ui.Line("")
|
||||
sizes[#sizes + 1] = ""
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -6,7 +6,7 @@ function M:peek()
|
||||
ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true })
|
||||
elseif err and not err:find("cancelled", 1, true) then
|
||||
ya.preview_widgets(self, {
|
||||
ui.Text(ui.Line(err):reverse()):area(self.area),
|
||||
ui.Text(err):area(self.area):reverse(),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
@ -1,6 +1,6 @@
|
||||
local M = {}
|
||||
|
||||
function M:msg(s) ya.preview_widgets(self, { ui.Text(ui.Line(s):reverse()):area(self.area):wrap(ui.Text.WRAP) }) end
|
||||
function M:msg(s) ya.preview_widgets(self, { ui.Text(s):area(self.area):reverse():wrap(ui.Text.WRAP) }) end
|
||||
|
||||
function M:peek()
|
||||
local path = tostring(self.file.url)
|
||||
|
@ -4,14 +4,14 @@ function M:peek()
|
||||
local cmd = os.getenv("YAZI_FILE_ONE") or "file"
|
||||
local output, code = Command(cmd):args({ "-bL", tostring(self.file.url) }):stdout(Command.PIPED):output()
|
||||
|
||||
local p
|
||||
local text
|
||||
if output then
|
||||
p = ui.Text.parse("----- File Type Classification -----\n\n" .. output.stdout):area(self.area)
|
||||
text = ui.Text.parse("----- File Type Classification -----\n\n" .. output.stdout)
|
||||
else
|
||||
p = ui.Text(string.format("Spawn `%s` command returns %s", cmd, code)):area(self.area)
|
||||
text = ui.Text(string.format("Spawn `%s` command returns %s", cmd, code))
|
||||
end
|
||||
|
||||
ya.preview_widgets(self, { p:wrap(ui.Text.WRAP) })
|
||||
ya.preview_widgets(self, { text:area(self.area):wrap(ui.Text.WRAP) })
|
||||
end
|
||||
|
||||
function M:seek() end
|
||||
|
@ -13,7 +13,7 @@ function M:peek()
|
||||
|
||||
if #folder.files == 0 then
|
||||
return ya.preview_widgets(self, {
|
||||
ui.Text(ui.Line(folder.stage.is_loading and "Loading..." or "No items")):area(self.area):align(ui.Text.CENTER),
|
||||
ui.Text(folder.stage.is_loading and "Loading..." or "No items"):area(self.area):align(ui.Text.CENTER),
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -3,7 +3,7 @@ use ratatui::widgets::Borders;
|
||||
|
||||
use super::{Rect, Renderable};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Bar {
|
||||
area: Rect,
|
||||
|
||||
@ -14,14 +14,8 @@ pub struct Bar {
|
||||
|
||||
impl Bar {
|
||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||
let new = lua.create_function(|_, (_, area, direction): (Table, Rect, u8)| {
|
||||
Ok(Self {
|
||||
area,
|
||||
|
||||
direction: Borders::from_bits_truncate(direction),
|
||||
symbol: Default::default(),
|
||||
style: Default::default(),
|
||||
})
|
||||
let new = lua.create_function(|_, (_, direction): (Table, u8)| {
|
||||
Ok(Self { direction: Borders::from_bits_truncate(direction), ..Default::default() })
|
||||
})?;
|
||||
|
||||
let bar = lua.create_table_from([
|
||||
|
@ -22,9 +22,8 @@ pub struct Border {
|
||||
|
||||
impl Border {
|
||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||
let new = lua.create_function(|_, (_, area, position): (Table, Rect, u8)| {
|
||||
let new = lua.create_function(|_, (_, position): (Table, u8)| {
|
||||
Ok(Border {
|
||||
area,
|
||||
position: ratatui::widgets::Borders::from_bits_truncate(position),
|
||||
..Default::default()
|
||||
})
|
||||
|
@ -1,42 +1,11 @@
|
||||
use mlua::{FromLua, Lua, Table, UserData};
|
||||
|
||||
#[derive(Clone, Copy, FromLua)]
|
||||
#[derive(Clone, Copy, Default, FromLua)]
|
||||
pub struct Constraint(pub(super) ratatui::layout::Constraint);
|
||||
|
||||
impl Constraint {
|
||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||
let constraint = lua.create_table_from([
|
||||
(
|
||||
"Min",
|
||||
lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Min(n))))?,
|
||||
),
|
||||
(
|
||||
"Max",
|
||||
lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Max(n))))?,
|
||||
),
|
||||
(
|
||||
"Length",
|
||||
lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Length(n))))?,
|
||||
),
|
||||
(
|
||||
"Percentage",
|
||||
lua.create_function(|_, n: u16| {
|
||||
Ok(Constraint(ratatui::layout::Constraint::Percentage(n)))
|
||||
})?,
|
||||
),
|
||||
(
|
||||
"Ratio",
|
||||
lua.create_function(|_, (a, b): (u32, u32)| {
|
||||
Ok(Constraint(ratatui::layout::Constraint::Ratio(a, b)))
|
||||
})?,
|
||||
),
|
||||
(
|
||||
"Fill",
|
||||
lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Fill(n))))?,
|
||||
),
|
||||
])?;
|
||||
|
||||
ui.raw_set("Constraint", constraint)
|
||||
pub fn install(_: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||
ui.raw_set("Constraint", Constraint::default())
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,4 +13,15 @@ impl From<Constraint> for ratatui::layout::Constraint {
|
||||
fn from(value: Constraint) -> Self { value.0 }
|
||||
}
|
||||
|
||||
impl UserData for Constraint {}
|
||||
impl UserData for Constraint {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
use ratatui::layout::Constraint as C;
|
||||
|
||||
methods.add_function("Min", |_, n: u16| Ok(Self(C::Min(n))));
|
||||
methods.add_function("Max", |_, n: u16| Ok(Self(C::Max(n))));
|
||||
methods.add_function("Length", |_, n: u16| Ok(Self(C::Length(n))));
|
||||
methods.add_function("Percentage", |_, n: u16| Ok(Self(C::Percentage(n))));
|
||||
methods.add_function("Ratio", |_, (a, b): (u32, u32)| Ok(Self(C::Ratio(a, b))));
|
||||
methods.add_function("Fill", |_, n: u16| Ok(Self(C::Fill(n))));
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,7 @@ pub struct Gauge {
|
||||
|
||||
impl Gauge {
|
||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||
ui.raw_set(
|
||||
"Gauge",
|
||||
lua.create_function(|_, area: Rect| Ok(Gauge { area, ..Default::default() }))?,
|
||||
)
|
||||
ui.raw_set("Gauge", lua.create_function(|_, ()| Ok(Gauge::default()))?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,49 +10,14 @@ const LEFT: u8 = 0;
|
||||
const CENTER: u8 = 1;
|
||||
const RIGHT: u8 = 2;
|
||||
|
||||
const EXPECTED: &str = "expected a string, ui.Span, ui.Line, or a table of them";
|
||||
|
||||
#[derive(Clone, FromLua)]
|
||||
pub struct Line(pub(super) ratatui::text::Line<'static>);
|
||||
|
||||
impl TryFrom<Table<'_>> for Line {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(tb: Table) -> Result<Self, Self::Error> {
|
||||
let seq: Vec<_> = tb.sequence_values().filter_map(|v| v.ok()).collect();
|
||||
let mut spans = Vec::with_capacity(seq.len());
|
||||
for value in seq {
|
||||
if let Value::UserData(ud) = value {
|
||||
if let Ok(span) = ud.take::<Span>() {
|
||||
spans.push(span.0);
|
||||
} else if let Ok(line) = ud.take::<Line>() {
|
||||
let style = line.0.style;
|
||||
spans.extend(line.0.spans.into_iter().map(|mut span| {
|
||||
span.style = style.patch(span.style);
|
||||
span
|
||||
}));
|
||||
} else {
|
||||
return Err("expected a table of Spans or Lines".into_lua_err());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Self(ratatui::text::Line::from(spans)))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<mlua::String<'_>> for Line {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(s: mlua::String) -> Result<Self, Self::Error> {
|
||||
Ok(Self(ratatui::text::Line::from(s.to_string_lossy().into_owned())))
|
||||
}
|
||||
}
|
||||
|
||||
impl Line {
|
||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||
let new = lua.create_function(|_, (_, value): (Table, Value)| match value {
|
||||
Value::Table(tb) => Line::try_from(tb),
|
||||
Value::String(s) => Line::try_from(s),
|
||||
_ => Err("expected a String, or a table of Spans and Lines".into_lua_err()),
|
||||
})?;
|
||||
let new = lua.create_function(|_, (_, value): (Table, Value)| Line::try_from(value))?;
|
||||
|
||||
let parse = lua.create_function(|_, code: mlua::String| {
|
||||
let Some(line) = code.as_bytes().split_inclusive(|&b| b == b'\n').next() else {
|
||||
@ -81,6 +46,53 @@ impl Line {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value<'_>> for Line {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(value: Value) -> Result<Self, Self::Error> {
|
||||
Ok(Self(match value {
|
||||
Value::Table(tb) => return Self::try_from(tb),
|
||||
Value::String(s) => s.to_string_lossy().into_owned().into(),
|
||||
Value::UserData(ud) => {
|
||||
if let Ok(span) = ud.take::<Span>() {
|
||||
span.0.into()
|
||||
} else if let Ok(mut line) = ud.take::<Line>() {
|
||||
line.0.spans.iter_mut().for_each(|s| s.style = line.0.style.patch(s.style));
|
||||
line.0
|
||||
} else {
|
||||
Err(EXPECTED.into_lua_err())?
|
||||
}
|
||||
}
|
||||
_ => Err(EXPECTED.into_lua_err())?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Table<'_>> for Line {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(tb: Table) -> Result<Self, Self::Error> {
|
||||
let mut spans = Vec::with_capacity(tb.raw_len());
|
||||
for v in tb.sequence_values() {
|
||||
match v? {
|
||||
Value::String(s) => spans.push(s.to_string_lossy().into_owned().into()),
|
||||
Value::UserData(ud) => {
|
||||
if let Ok(span) = ud.take::<Span>() {
|
||||
spans.push(span.0);
|
||||
} else if let Ok(mut line) = ud.take::<Line>() {
|
||||
line.0.spans.iter_mut().for_each(|s| s.style = line.0.style.patch(s.style));
|
||||
spans.extend(line.0.spans);
|
||||
} else {
|
||||
return Err(EXPECTED.into_lua_err());
|
||||
}
|
||||
}
|
||||
_ => Err(EXPECTED.into_lua_err())?,
|
||||
}
|
||||
}
|
||||
Ok(Self(spans.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for Line {
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
crate::impl_style_method!(methods, 0.style);
|
||||
|
@ -1,4 +1,4 @@
|
||||
use mlua::{Lua, Table, UserData, Value};
|
||||
use mlua::{ExternalError, Lua, Table, UserData, Value};
|
||||
use ratatui::widgets::Widget;
|
||||
|
||||
use super::{Rect, Renderable, Text};
|
||||
@ -15,10 +15,13 @@ impl List {
|
||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||
ui.raw_set(
|
||||
"List",
|
||||
lua.create_function(|_, values: Vec<Value>| {
|
||||
let mut items = Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
items.push(ratatui::widgets::ListItem::new(Text::try_from(value)?));
|
||||
lua.create_function(|_, tb: Table| {
|
||||
let mut items = Vec::with_capacity(tb.raw_len());
|
||||
for v in tb.sequence_values::<Value>() {
|
||||
match v? {
|
||||
Value::Table(_) => Err("Nested table not supported".into_lua_err())?,
|
||||
v => items.push(Text::try_from(v)?),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { inner: ratatui::widgets::List::new(items), ..Default::default() })
|
||||
|
@ -1,17 +1,32 @@
|
||||
use mlua::{FromLua, Lua, Table, UserData, UserDataMethods};
|
||||
use mlua::{ExternalError, FromLua, Lua, Table, UserData, UserDataMethods, Value};
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
|
||||
const EXPECTED: &str = "expected a string or ui.Span";
|
||||
|
||||
#[derive(Clone, FromLua)]
|
||||
pub struct Span(pub(super) ratatui::text::Span<'static>);
|
||||
|
||||
impl Span {
|
||||
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
|
||||
ui.raw_set(
|
||||
"Span",
|
||||
lua.create_function(|_, content: mlua::String| {
|
||||
Ok(Self(ratatui::text::Span::raw(content.to_string_lossy().into_owned())))
|
||||
})?,
|
||||
)
|
||||
ui.raw_set("Span", lua.create_function(|_, value: Value| Span::try_from(value))?)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value<'_>> for Span {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(value: Value<'_>) -> Result<Self, Self::Error> {
|
||||
Ok(Self(match value {
|
||||
Value::String(s) => s.to_string_lossy().into_owned().into(),
|
||||
Value::UserData(ud) => {
|
||||
if let Ok(span) = ud.take::<Span>() {
|
||||
span.0
|
||||
} else {
|
||||
Err(EXPECTED.into_lua_err())?
|
||||
}
|
||||
}
|
||||
_ => Err(EXPECTED.into_lua_err())?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,8 @@ pub const WRAP_NO: u8 = 0;
|
||||
pub const WRAP: u8 = 1;
|
||||
pub const WRAP_TRIM: u8 = 2;
|
||||
|
||||
const EXPECTED: &str = "expected a string, ui.Line, ui.Span or a table of them";
|
||||
|
||||
#[derive(Clone, Default, FromLua)]
|
||||
pub struct Text {
|
||||
pub area: Rect,
|
||||
@ -54,29 +56,45 @@ impl TryFrom<Value<'_>> for Text {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(value: Value) -> mlua::Result<Self> {
|
||||
match value {
|
||||
Value::String(s) => {
|
||||
Ok(Self { inner: s.to_string_lossy().into_owned().into(), ..Default::default() })
|
||||
}
|
||||
let inner = match value {
|
||||
Value::Table(tb) => return Self::try_from(tb),
|
||||
Value::String(s) => s.to_string_lossy().into_owned().into(),
|
||||
Value::UserData(ud) => {
|
||||
let inner: ratatui::text::Text = if let Ok(line) = ud.take::<Line>() {
|
||||
if let Ok(line) = ud.take::<Line>() {
|
||||
line.0.into()
|
||||
} else if let Ok(span) = ud.take::<Span>() {
|
||||
span.0.into()
|
||||
} else {
|
||||
return Err("expected a String, Line or Span".into_lua_err());
|
||||
};
|
||||
Ok(Self { inner, ..Default::default() })
|
||||
}
|
||||
Value::Table(tb) => {
|
||||
let mut lines = Vec::with_capacity(tb.raw_len());
|
||||
for v in tb.sequence_values::<Value>() {
|
||||
lines.extend(Self::try_from(v?)?.inner.lines);
|
||||
Err(EXPECTED.into_lua_err())?
|
||||
}
|
||||
Ok(Self { inner: lines.into(), ..Default::default() })
|
||||
}
|
||||
_ => Err("expected a String, Line, Span or a Table of them".into_lua_err()),
|
||||
_ => Err(EXPECTED.into_lua_err())?,
|
||||
};
|
||||
Ok(Self { inner, ..Default::default() })
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Table<'_>> for Text {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(tb: Table<'_>) -> Result<Self, Self::Error> {
|
||||
let mut lines = Vec::with_capacity(tb.raw_len());
|
||||
for v in tb.sequence_values() {
|
||||
match v? {
|
||||
Value::String(s) => lines.push(s.to_string_lossy().into_owned().into()),
|
||||
Value::UserData(ud) => {
|
||||
if let Ok(span) = ud.take::<Span>() {
|
||||
lines.push(span.0.into());
|
||||
} else if let Ok(line) = ud.take::<Line>() {
|
||||
lines.push(line.0);
|
||||
} else {
|
||||
return Err(EXPECTED.into_lua_err());
|
||||
}
|
||||
}
|
||||
_ => Err(EXPECTED.into_lua_err())?,
|
||||
}
|
||||
}
|
||||
Ok(Self { inner: lines.into(), ..Default::default() })
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +104,7 @@ impl From<Text> for ratatui::text::Text<'static> {
|
||||
|
||||
impl From<Text> for ratatui::widgets::Paragraph<'static> {
|
||||
fn from(value: Text) -> Self {
|
||||
let align = value.inner.alignment.unwrap_or(ratatui::layout::Alignment::Left);
|
||||
let align = value.inner.alignment.unwrap_or_default();
|
||||
let mut p = ratatui::widgets::Paragraph::new(value.inner);
|
||||
|
||||
if value.wrap != WRAP_NO {
|
||||
|
Loading…
Reference in New Issue
Block a user