TextInput fields with color encodings. Draw shades.
This commit is contained in:
parent
b1328f193c
commit
39fd8ad9e9
@ -1,11 +1,14 @@
|
|||||||
use iced::{
|
use iced::{
|
||||||
canvas, slider, Canvas, Color, Column, Element, Length, Point, Row,
|
canvas, slider, text_input, Canvas, Color, Column, Element, Length, Point,
|
||||||
Sandbox, Settings, Slider, Text,
|
Row, Sandbox, Settings, Slider, Text, TextInput,
|
||||||
};
|
};
|
||||||
use iced_core::palette::{self, Limited};
|
use iced_core::palette::{self, Limited};
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
ColorPalette::run(Settings::default())
|
ColorPalette::run(Settings {
|
||||||
|
antialiasing: true,
|
||||||
|
..Settings::default()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -56,6 +59,18 @@ pub struct ColorPalette {
|
|||||||
hwb_sliders: [slider::State; 3],
|
hwb_sliders: [slider::State; 3],
|
||||||
lab_sliders: [slider::State; 3],
|
lab_sliders: [slider::State; 3],
|
||||||
lch_sliders: [slider::State; 3],
|
lch_sliders: [slider::State; 3],
|
||||||
|
rgb_text_state: text_input::State,
|
||||||
|
hsl_text_state: text_input::State,
|
||||||
|
hsv_text_state: text_input::State,
|
||||||
|
hwb_text_state: text_input::State,
|
||||||
|
lab_text_state: text_input::State,
|
||||||
|
lch_text_state: text_input::State,
|
||||||
|
rgb_text_value: String,
|
||||||
|
hsl_text_value: String,
|
||||||
|
hsv_text_value: String,
|
||||||
|
hwb_text_value: String,
|
||||||
|
lab_text_value: String,
|
||||||
|
lch_text_value: String,
|
||||||
canvas_layer: canvas::layer::Cache<State>,
|
canvas_layer: canvas::layer::Cache<State>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +82,7 @@ pub enum Message {
|
|||||||
HwbColorChanged(palette::Hwb),
|
HwbColorChanged(palette::Hwb),
|
||||||
LabColorChanged(palette::Lab),
|
LabColorChanged(palette::Lab),
|
||||||
LchColorChanged(palette::Lch),
|
LchColorChanged(palette::Lch),
|
||||||
|
TextInput,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sandbox for ColorPalette {
|
impl Sandbox for ColorPalette {
|
||||||
@ -81,14 +97,34 @@ impl Sandbox for ColorPalette {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let state = State::new();
|
||||||
|
let rgb_text_value = color_str(&state.color, ColorFormat::Rgb);
|
||||||
|
let hsl_text_value = color_str(&state.color, ColorFormat::Hsl);
|
||||||
|
let hsv_text_value = color_str(&state.color, ColorFormat::Hsv);
|
||||||
|
let hwb_text_value = color_str(&state.color, ColorFormat::Hwb);
|
||||||
|
let lab_text_value = color_str(&state.color, ColorFormat::Lab);
|
||||||
|
let lch_text_value = color_str(&state.color, ColorFormat::Lch);
|
||||||
|
|
||||||
ColorPalette {
|
ColorPalette {
|
||||||
state: State::new(),
|
state,
|
||||||
rgb_sliders: triple_slider(),
|
rgb_sliders: triple_slider(),
|
||||||
hsl_sliders: triple_slider(),
|
hsl_sliders: triple_slider(),
|
||||||
hsv_sliders: triple_slider(),
|
hsv_sliders: triple_slider(),
|
||||||
hwb_sliders: triple_slider(),
|
hwb_sliders: triple_slider(),
|
||||||
lab_sliders: triple_slider(),
|
lab_sliders: triple_slider(),
|
||||||
lch_sliders: triple_slider(),
|
lch_sliders: triple_slider(),
|
||||||
|
rgb_text_state: text_input::State::new(),
|
||||||
|
hsl_text_state: text_input::State::new(),
|
||||||
|
hsv_text_state: text_input::State::new(),
|
||||||
|
hwb_text_state: text_input::State::new(),
|
||||||
|
lab_text_state: text_input::State::new(),
|
||||||
|
lch_text_state: text_input::State::new(),
|
||||||
|
rgb_text_value,
|
||||||
|
hsl_text_value,
|
||||||
|
hsv_text_value,
|
||||||
|
hwb_text_value,
|
||||||
|
lab_text_value,
|
||||||
|
lch_text_value,
|
||||||
canvas_layer: canvas::layer::Cache::new(),
|
canvas_layer: canvas::layer::Cache::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,6 +134,11 @@ impl Sandbox for ColorPalette {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) {
|
fn update(&mut self, message: Message) {
|
||||||
|
match message {
|
||||||
|
Message::TextInput => return,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
let mut srgb = match message {
|
let mut srgb = match message {
|
||||||
Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb),
|
Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb),
|
||||||
Message::HslColorChanged(hsl) => palette::Srgb::from(hsl),
|
Message::HslColorChanged(hsl) => palette::Srgb::from(hsl),
|
||||||
@ -105,6 +146,7 @@ impl Sandbox for ColorPalette {
|
|||||||
Message::HwbColorChanged(hwb) => palette::Srgb::from(hwb),
|
Message::HwbColorChanged(hwb) => palette::Srgb::from(hwb),
|
||||||
Message::LabColorChanged(lab) => palette::Srgb::from(lab),
|
Message::LabColorChanged(lab) => palette::Srgb::from(lab),
|
||||||
Message::LchColorChanged(lch) => palette::Srgb::from(lch),
|
Message::LchColorChanged(lch) => palette::Srgb::from(lch),
|
||||||
|
_ => return,
|
||||||
};
|
};
|
||||||
srgb.clamp_self();
|
srgb.clamp_self();
|
||||||
self.canvas_layer.clear();
|
self.canvas_layer.clear();
|
||||||
@ -112,6 +154,14 @@ impl Sandbox for ColorPalette {
|
|||||||
|
|
||||||
// Set theme colors
|
// Set theme colors
|
||||||
self.state.theme = generate_theme(&self.state.color);
|
self.state.theme = generate_theme(&self.state.color);
|
||||||
|
|
||||||
|
// Set text
|
||||||
|
self.rgb_text_value = color_str(&self.state.color, ColorFormat::Rgb);
|
||||||
|
self.hsl_text_value = color_str(&self.state.color, ColorFormat::Hsl);
|
||||||
|
self.hsv_text_value = color_str(&self.state.color, ColorFormat::Hsv);
|
||||||
|
self.hwb_text_value = color_str(&self.state.color, ColorFormat::Hwb);
|
||||||
|
self.lab_text_value = color_str(&self.state.color, ColorFormat::Lab);
|
||||||
|
self.lch_text_value = color_str(&self.state.color, ColorFormat::Lch);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&mut self) -> Element<Message> {
|
fn view(&mut self) -> Element<Message> {
|
||||||
@ -131,12 +181,12 @@ impl Sandbox for ColorPalette {
|
|||||||
let lch = palette::Lch::from(srgb);
|
let lch = palette::Lch::from(srgb);
|
||||||
|
|
||||||
Column::new()
|
Column::new()
|
||||||
.padding(20)
|
.padding(10)
|
||||||
.spacing(20)
|
.spacing(10)
|
||||||
.push(
|
.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.push(Text::new("RGB"))
|
.push(Text::new("RGB").width(Length::Units(50)))
|
||||||
.push(Slider::new(rgb1, 0.0..=1.0, color.r, move |r| {
|
.push(Slider::new(rgb1, 0.0..=1.0, color.r, move |r| {
|
||||||
Message::RgbColorChanged(Color { r, ..color })
|
Message::RgbColorChanged(Color { r, ..color })
|
||||||
}))
|
}))
|
||||||
@ -145,12 +195,23 @@ impl Sandbox for ColorPalette {
|
|||||||
}))
|
}))
|
||||||
.push(Slider::new(rgb3, 0.0..=1.0, color.b, move |b| {
|
.push(Slider::new(rgb3, 0.0..=1.0, color.b, move |b| {
|
||||||
Message::RgbColorChanged(Color { b, ..color })
|
Message::RgbColorChanged(Color { b, ..color })
|
||||||
})),
|
}))
|
||||||
|
.push(
|
||||||
|
TextInput::new(
|
||||||
|
&mut self.rgb_text_state,
|
||||||
|
"",
|
||||||
|
&mut self.rgb_text_value,
|
||||||
|
|_s| Message::TextInput,
|
||||||
|
)
|
||||||
|
.width(Length::Units(150))
|
||||||
|
.size(14)
|
||||||
|
.padding(2),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.push(Text::new("HSL"))
|
.push(Text::new("HSL").width(Length::Units(50)))
|
||||||
.push(Slider::new(
|
.push(Slider::new(
|
||||||
hsl1,
|
hsl1,
|
||||||
0.0..=360.0,
|
0.0..=360.0,
|
||||||
@ -183,12 +244,23 @@ impl Sandbox for ColorPalette {
|
|||||||
..hsl
|
..hsl
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)),
|
))
|
||||||
|
.push(
|
||||||
|
TextInput::new(
|
||||||
|
&mut self.hsl_text_state,
|
||||||
|
"",
|
||||||
|
&mut self.hsl_text_value,
|
||||||
|
|_s| Message::TextInput,
|
||||||
|
)
|
||||||
|
.width(Length::Units(150))
|
||||||
|
.size(14)
|
||||||
|
.padding(2),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.push(Text::new("HSV"))
|
.push(Text::new("HSV").width(Length::Units(50)))
|
||||||
.push(Slider::new(
|
.push(Slider::new(
|
||||||
hsv1,
|
hsv1,
|
||||||
0.0..=360.0,
|
0.0..=360.0,
|
||||||
@ -221,12 +293,23 @@ impl Sandbox for ColorPalette {
|
|||||||
..hsv
|
..hsv
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)),
|
))
|
||||||
|
.push(
|
||||||
|
TextInput::new(
|
||||||
|
&mut self.hsv_text_state,
|
||||||
|
"",
|
||||||
|
&mut self.hsv_text_value,
|
||||||
|
|_s| Message::TextInput,
|
||||||
|
)
|
||||||
|
.width(Length::Units(150))
|
||||||
|
.size(14)
|
||||||
|
.padding(2),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.push(Text::new("HWB"))
|
.push(Text::new("HWB").width(Length::Units(50)))
|
||||||
.push(Slider::new(
|
.push(Slider::new(
|
||||||
hwb1,
|
hwb1,
|
||||||
0.0..=360.0,
|
0.0..=360.0,
|
||||||
@ -259,12 +342,23 @@ impl Sandbox for ColorPalette {
|
|||||||
..hwb
|
..hwb
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)),
|
))
|
||||||
|
.push(
|
||||||
|
TextInput::new(
|
||||||
|
&mut self.hwb_text_state,
|
||||||
|
"",
|
||||||
|
&mut self.hwb_text_value,
|
||||||
|
|_s| Message::TextInput,
|
||||||
|
)
|
||||||
|
.width(Length::Units(150))
|
||||||
|
.size(14)
|
||||||
|
.padding(2),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.push(Text::new("Lab"))
|
.push(Text::new("Lab").width(Length::Units(50)))
|
||||||
.push(Slider::new(lab1, 0.0..=100.0, lab.l, move |l| {
|
.push(Slider::new(lab1, 0.0..=100.0, lab.l, move |l| {
|
||||||
Message::LabColorChanged(palette::Lab { l, ..lab })
|
Message::LabColorChanged(palette::Lab { l, ..lab })
|
||||||
}))
|
}))
|
||||||
@ -273,12 +367,23 @@ impl Sandbox for ColorPalette {
|
|||||||
}))
|
}))
|
||||||
.push(Slider::new(lab3, -128.0..=127.0, lab.b, move |b| {
|
.push(Slider::new(lab3, -128.0..=127.0, lab.b, move |b| {
|
||||||
Message::LabColorChanged(palette::Lab { b, ..lab })
|
Message::LabColorChanged(palette::Lab { b, ..lab })
|
||||||
})),
|
}))
|
||||||
|
.push(
|
||||||
|
TextInput::new(
|
||||||
|
&mut self.lab_text_state,
|
||||||
|
"",
|
||||||
|
&mut self.lab_text_value,
|
||||||
|
|_s| Message::TextInput,
|
||||||
|
)
|
||||||
|
.width(Length::Units(150))
|
||||||
|
.size(14)
|
||||||
|
.padding(2),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(10)
|
.spacing(10)
|
||||||
.push(Text::new("Lch"))
|
.push(Text::new("Lch").width(Length::Units(50)))
|
||||||
.push(Slider::new(lch1, 0.0..=100.0, lch.l, move |l| {
|
.push(Slider::new(lch1, 0.0..=100.0, lch.l, move |l| {
|
||||||
Message::LchColorChanged(palette::Lch { l, ..lch })
|
Message::LchColorChanged(palette::Lch { l, ..lch })
|
||||||
}))
|
}))
|
||||||
@ -303,12 +408,24 @@ impl Sandbox for ColorPalette {
|
|||||||
..lch
|
..lch
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)),
|
))
|
||||||
|
.push(
|
||||||
|
TextInput::new(
|
||||||
|
&mut self.lch_text_state,
|
||||||
|
"",
|
||||||
|
&mut self.lch_text_value,
|
||||||
|
|_s| Message::TextInput,
|
||||||
|
)
|
||||||
|
.width(Length::Units(150))
|
||||||
|
.size(14)
|
||||||
|
.padding(2),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
Canvas::new()
|
Canvas::new()
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Units(150))
|
// .height(Length::Units(250))
|
||||||
|
.height(Length::Fill)
|
||||||
.push(self.canvas_layer.with(&self.state)),
|
.push(self.canvas_layer.with(&self.state)),
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
@ -317,7 +434,7 @@ impl Sandbox for ColorPalette {
|
|||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn new() -> State {
|
pub fn new() -> State {
|
||||||
let base = Color::from_rgb8(27, 135, 199);
|
let base = Color::from_rgb8(75, 128, 190);
|
||||||
State {
|
State {
|
||||||
color: base,
|
color: base,
|
||||||
theme: generate_theme(&base),
|
theme: generate_theme(&base),
|
||||||
@ -328,6 +445,7 @@ impl State {
|
|||||||
impl canvas::Drawable for State {
|
impl canvas::Drawable for State {
|
||||||
fn draw(&self, frame: &mut canvas::Frame) {
|
fn draw(&self, frame: &mut canvas::Frame) {
|
||||||
use canvas::{Fill, Path};
|
use canvas::{Fill, Path};
|
||||||
|
use iced::{HorizontalAlignment, VerticalAlignment};
|
||||||
use palette::{Hsl, Srgb};
|
use palette::{Hsl, Srgb};
|
||||||
|
|
||||||
if self.theme.len() == 0 {
|
if self.theme.len() == 0 {
|
||||||
@ -335,10 +453,16 @@ impl canvas::Drawable for State {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let pad = 5.0;
|
let pad = 20.0;
|
||||||
|
|
||||||
let box_width = frame.width() / self.theme.len() as f32;
|
let box_width = frame.width() / self.theme.len() as f32;
|
||||||
let box_height = frame.height() / 2.0 - pad;
|
let box_height = frame.height() / 2.0 - pad;
|
||||||
|
|
||||||
|
let mut text = canvas::Text::default();
|
||||||
|
text.horizontal_alignment = HorizontalAlignment::Left;
|
||||||
|
text.vertical_alignment = VerticalAlignment::Top;
|
||||||
|
text.size = 15.0;
|
||||||
|
|
||||||
for i in 0..self.theme.len() {
|
for i in 0..self.theme.len() {
|
||||||
let anchor = Point {
|
let anchor = Point {
|
||||||
x: (i as f32) * box_width,
|
x: (i as f32) * box_width,
|
||||||
@ -360,6 +484,57 @@ impl canvas::Drawable for State {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
frame.fill(&rect, Fill::Color(self.theme[i]));
|
frame.fill(&rect, Fill::Color(self.theme[i]));
|
||||||
|
|
||||||
|
if self.theme[i] == self.color {
|
||||||
|
let cx = anchor.x + box_width / 2.0;
|
||||||
|
let tri_w = 10.0;
|
||||||
|
|
||||||
|
let tri = Path::new(|path| {
|
||||||
|
path.move_to(Point {
|
||||||
|
x: cx - tri_w,
|
||||||
|
y: 0.0,
|
||||||
|
});
|
||||||
|
path.line_to(Point {
|
||||||
|
x: cx + tri_w,
|
||||||
|
y: 0.0,
|
||||||
|
});
|
||||||
|
path.line_to(Point { x: cx, y: tri_w });
|
||||||
|
path.line_to(Point {
|
||||||
|
x: cx - tri_w,
|
||||||
|
y: 0.0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
frame.fill(&tri, Fill::Color(Color::WHITE));
|
||||||
|
|
||||||
|
let tri = Path::new(|path| {
|
||||||
|
path.move_to(Point {
|
||||||
|
x: cx - tri_w,
|
||||||
|
y: box_height,
|
||||||
|
});
|
||||||
|
path.line_to(Point {
|
||||||
|
x: cx + tri_w,
|
||||||
|
y: box_height,
|
||||||
|
});
|
||||||
|
path.line_to(Point {
|
||||||
|
x: cx,
|
||||||
|
y: box_height - tri_w,
|
||||||
|
});
|
||||||
|
path.line_to(Point {
|
||||||
|
x: cx - tri_w,
|
||||||
|
y: box_height,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
frame.fill(&tri, Fill::Color(Color::WHITE));
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.fill_text(canvas::Text {
|
||||||
|
content: color_str(&self.theme[i], ColorFormat::Hex),
|
||||||
|
position: Point {
|
||||||
|
x: anchor.x,
|
||||||
|
y: box_height,
|
||||||
|
},
|
||||||
|
..text
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let hsl = Hsl::from(Srgb::from(self.color));
|
let hsl = Hsl::from(Srgb::from(self.color));
|
||||||
@ -391,6 +566,76 @@ impl canvas::Drawable for State {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
frame.fill(&rect, Fill::Color(color));
|
frame.fill(&rect, Fill::Color(color));
|
||||||
|
|
||||||
|
frame.fill_text(canvas::Text {
|
||||||
|
content: color_str(&color, ColorFormat::Hex),
|
||||||
|
position: Point {
|
||||||
|
x: anchor.x,
|
||||||
|
y: box_height + 2.0 * pad - 15.0,
|
||||||
|
},
|
||||||
|
..text
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ColorFormat {
|
||||||
|
Hex,
|
||||||
|
Rgb,
|
||||||
|
Hsl,
|
||||||
|
Hsv,
|
||||||
|
Hwb,
|
||||||
|
Lab,
|
||||||
|
Lch,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn color_str(color: &Color, color_format: ColorFormat) -> String {
|
||||||
|
let srgb = palette::Srgb::from(*color);
|
||||||
|
let hsl = palette::Hsl::from(srgb);
|
||||||
|
let hsv = palette::Hsv::from(srgb);
|
||||||
|
let hwb = palette::Hwb::from(srgb);
|
||||||
|
let lab = palette::Lab::from(srgb);
|
||||||
|
let lch = palette::Lch::from(srgb);
|
||||||
|
|
||||||
|
match color_format {
|
||||||
|
ColorFormat::Hex => format!(
|
||||||
|
"#{:x}{:x}{:x}",
|
||||||
|
(255.0 * color.r).round() as u8,
|
||||||
|
(255.0 * color.g).round() as u8,
|
||||||
|
(255.0 * color.b).round() as u8
|
||||||
|
),
|
||||||
|
ColorFormat::Rgb => format!(
|
||||||
|
"rgb({:.0}, {:.0}, {:.0})",
|
||||||
|
255.0 * color.r,
|
||||||
|
255.0 * color.g,
|
||||||
|
255.0 * color.b
|
||||||
|
),
|
||||||
|
ColorFormat::Hsl => format!(
|
||||||
|
"hsl({:.1}, {:.1}%, {:.1}%)",
|
||||||
|
hsl.hue.to_positive_degrees(),
|
||||||
|
100.0 * hsl.saturation,
|
||||||
|
100.0 * hsl.lightness
|
||||||
|
),
|
||||||
|
ColorFormat::Hsv => format!(
|
||||||
|
"hsv({:.1}, {:.1}%, {:.1}%)",
|
||||||
|
hsv.hue.to_positive_degrees(),
|
||||||
|
100.0 * hsv.saturation,
|
||||||
|
100.0 * hsv.value
|
||||||
|
),
|
||||||
|
ColorFormat::Hwb => format!(
|
||||||
|
"hwb({:.1}, {:.1}%, {:.1}%)",
|
||||||
|
hwb.hue.to_positive_degrees(),
|
||||||
|
100.0 * hwb.whiteness,
|
||||||
|
100.0 * hwb.blackness
|
||||||
|
),
|
||||||
|
ColorFormat::Lab => {
|
||||||
|
format!("Lab({:.1}, {:.1}, {:.1})", lab.l, lab.a, lab.b)
|
||||||
|
}
|
||||||
|
ColorFormat::Lch => format!(
|
||||||
|
"Lch({:.1}, {:.1}, {:.1})",
|
||||||
|
lch.l,
|
||||||
|
lch.chroma,
|
||||||
|
lch.hue.to_positive_degrees()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user