use fltk::{
app::{App, Scheme},
button::Button,
enums::{Color, Event, Font},
group::*,
menu::Choice,
misc::Spinner,
prelude::*,
text::{StyleTableEntryExt, TextAttr, TextBuffer, TextEditor},
window::Window,
};
use std::{cell::RefCell, char, rc::Rc};
struct Style {
style_table: Vec<StyleTableEntryExt>,
}
impl Style {
fn new() -> Style {
Style {
style_table: Vec::<StyleTableEntryExt>::new(),
}
}
#[allow(clippy::too_many_arguments)]
fn apply_style(
&mut self,
pos: Option<i32>,
ins_items: Option<i32>,
del_items: Option<i32>,
repl_start: Option<i32>,
repl_end: Option<i32>,
font: Font,
size: i32,
color: Color,
attr: TextAttr,
text_editor: &mut TextEditor,
) {
let mut style_buffer = match text_editor.style_buffer() {
Some(sb) => sb,
None => TextBuffer::default(),
};
let style_char =
match self.style_table.iter().position(|s| {
s.font == font && s.size == size && s.color == color && s.attr == attr
}) {
Some(i) => ((i + 65) as u8 as char).to_string(),
None => {
self.style_table.push(StyleTableEntryExt {
color,
font,
size,
attr,
bgcolor: Color::Black,
});
((self.style_table.len() + 64) as u8 as char).to_string()
}
};
match ins_items {
Some(n) if n > 0 => {
style_buffer.insert(pos.unwrap(), style_char.repeat(n as usize).as_str());
}
_ => match del_items {
Some(n) if n > 0 => {
style_buffer.remove(pos.unwrap(), pos.unwrap() + n);
}
_ => match repl_end {
Some(n) if n > 0 => {
style_buffer.replace(
repl_start.unwrap(),
repl_end.unwrap(),
style_char
.repeat((repl_end.unwrap() - repl_start.unwrap()) as usize)
.as_str(),
);
}
_ => {}
},
},
}
let mut style_index = style_buffer
.text()
.chars()
.map(|c| (c as usize) - 65)
.collect::<Vec<usize>>();
style_index.sort_unstable();
style_index.dedup();
for (i, &v) in style_index.iter().enumerate() {
self.style_table.swap(i, v);
style_buffer.set_text(
style_buffer
.text()
.replace(
(v + 65) as u8 as char,
((i + 65) as u8 as char).to_string().as_str(),
)
.as_str(),
);
}
self.style_table.truncate(style_index.len());
text_editor.set_highlight_data_ext(style_buffer, self.style_table.to_owned());
}
}
fn main() {
let style = Rc::from(RefCell::from(Style::new()));
let app = App::default().with_scheme(Scheme::Gleam);
let mut wind = Window::default()
.with_size(500, 200)
.with_label("Highlight");
let mut vpack = Pack::new(4, 4, 492, 192, "");
vpack.set_spacing(4);
let mut text_editor = TextEditor::default().with_size(492, 163);
let mut hpack = Pack::new(4, 4, 492, 25, "").with_type(PackType::Horizontal);
hpack.set_spacing(8);
let mut font = Choice::default().with_size(130, 25);
let mut choice = Choice::default().with_size(130, 25);
let mut size = Spinner::default().with_size(60, 25);
let mut color = Choice::default().with_size(100, 25);
let mut btn_clear = Button::default().with_size(40, 25).with_label("X");
hpack.end();
vpack.end();
wind.end();
wind.show();
text_editor.wrap_mode(fltk::text::WrapMode::AtBounds, 0);
text_editor.set_buffer(TextBuffer::default());
font.add_choice("Courier|Helvetica|Times");
font.set_value(0);
font.set_tooltip("Font");
choice.add_choice("Normal|Underline|Strike");
choice.set_value(0);
size.set_value(18.0);
size.set_step(1.0);
size.set_range(12.0, 28.0);
size.set_tooltip("Size");
color.set_tooltip("Color");
color.add_choice("#000000|#ff0000|#00ff00|#0000ff|#ffff00|#00ffff");
color.set_value(0);
btn_clear.set_label_color(Color::Red);
btn_clear.set_tooltip("Clear style");
for mut item in color.clone() {
if let Some(lbl) = item.label() {
item.set_label_color(Color::from_u32(
u32::from_str_radix(lbl.trim().strip_prefix('#').unwrap(), 16)
.ok()
.unwrap(),
));
}
}
let style_rc1 = Rc::clone(&style);
text_editor.buffer().unwrap().add_modify_callback({
let mut text_editor1 = text_editor.clone();
let font1 = font.clone();
let size1 = size.clone();
let color1 = color.clone();
let choice1 = choice.clone();
move |pos: i32, ins_items: i32, del_items: i32, _: i32, _: &str| {
let attr = if choice1.value() == 1 {
TextAttr::Underline
} else if choice1.value() == 2 {
TextAttr::StrikeThrough
} else {
TextAttr::None
};
if ins_items > 0 || del_items > 0 {
let mut style = style_rc1.borrow_mut();
let color = Color::from_u32(
u32::from_str_radix(
color1
.text(color1.value())
.unwrap()
.trim()
.strip_prefix('#')
.unwrap(),
16,
)
.ok()
.unwrap(),
);
style.apply_style(
Some(pos),
Some(ins_items),
Some(del_items),
None,
None,
Font::by_name(font1.text(font1.value()).unwrap().trim()),
size1.value() as i32,
color,
attr,
&mut text_editor1,
);
}
}
});
color.set_callback({
let size = size.clone();
let font = font.clone();
let choice = choice.clone();
let mut text_editor = text_editor.clone();
let style_rc1 = Rc::clone(&style);
move |color| {
let attr = match choice.value() {
0 => TextAttr::None,
1 => TextAttr::Underline,
2 => TextAttr::StrikeThrough,
_ => unreachable!(),
};
if let Some(buf) = text_editor.buffer() {
if let Some((s, e)) = buf.selection_position() {
let mut style = style_rc1.borrow_mut();
let color = Color::from_u32(
u32::from_str_radix(
color
.text(color.value())
.unwrap()
.trim()
.strip_prefix('#')
.unwrap(),
16,
)
.ok()
.unwrap(),
);
style.apply_style(
None,
None,
None,
Some(s),
Some(e),
Font::by_name(font.text(font.value()).unwrap().trim()),
size.value() as i32,
color,
attr,
&mut text_editor,
);
}
}
}
});
text_editor.handle({
let style_rc1 = Rc::clone(&style);
let mut font1 = font.clone();
let mut size1 = size.clone();
let mut color1 = color.clone();
move |te, e| match e {
Event::KeyUp | Event::Released => {
if let Some(buff) = te.style_buffer() {
let i = te.insert_position();
if let Some(t) = buff.text_range(i, i + 1) {
if !t.is_empty() {
let style = style_rc1.borrow_mut();
if let Some(i) = t.chars().next().map(|c| (c as usize - 65)) {
if let Some(style) = style.style_table.get(i) {
if let Some(mn) = font1.find_item(&format!("{:?}", style.font))
{
font1.set_item(&mn);
}
size1.set_value(style.size as f64);
let (r, g, b) = style.color.to_rgb();
if let Some(mn) =
color1.find_item(format!("{r:02x}{g:02x}{b:02x}").as_str())
{
color1.set_item(&mn);
}
}
}
}
}
}
true
}
_ => false,
}
});
choice.set_callback({
let mut color1 = color.clone();
move |_| color1.do_callback()
});
font.set_callback({
let mut color1 = color.clone();
move |_| color1.do_callback()
});
size.set_callback({
let mut color1 = color.clone();
move |_| color1.do_callback()
});
btn_clear.set_callback({
let style_rc1 = Rc::clone(&style);
let text_editor1 = text_editor.clone();
move |_| {
match text_editor1.buffer().unwrap().selection_position() {
Some((_, _)) => {
font.set_value(0);
size.set_value(18.0);
color.set_value(0);
choice.set_value(0);
color.do_callback();
}
None => {
font.set_value(0);
size.set_value(18.0);
color.set_value(0);
style_rc1.borrow_mut().apply_style(
None,
None,
None,
Some(0),
Some(text_editor1.buffer().unwrap().length()),
Font::Courier,
16,
Color::Black,
TextAttr::None,
&mut text_editor,
);
}
};
}
});
app.run().unwrap();
}