mirror of
https://github.com/YGGverse/Yoda.git
synced 2026-03-31 16:45:27 +00:00
initial code widget implementation
This commit is contained in:
parent
e92eb318b3
commit
fe467e8316
4 changed files with 131 additions and 26 deletions
|
|
@ -101,7 +101,7 @@ impl Tags {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render placeholders
|
// Render placeholders
|
||||||
self.code.render(&buffer);
|
self.code.render(text_view);
|
||||||
|
|
||||||
// Format document title string
|
// Format document title string
|
||||||
title.map(|mut s| {
|
title.map(|mut s| {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,13 @@ mod ansi;
|
||||||
mod syntax;
|
mod syntax;
|
||||||
|
|
||||||
use gtk::{
|
use gtk::{
|
||||||
TextBuffer, TextSearchFlags, TextTag, WrapMode,
|
Align, Box, Button, Label, Orientation, PolicyType, ScrolledWindow, Separator, TextBuffer,
|
||||||
glib::{GString, uuid_string_random},
|
TextSearchFlags, TextTag, TextTagTable, TextView, WrapMode,
|
||||||
prelude::{TextBufferExt, TextBufferExtManual},
|
gdk::Display,
|
||||||
|
glib::{ControlFlow, GString, idle_add_local, uuid_string_random},
|
||||||
|
prelude::{
|
||||||
|
BoxExt, ButtonExt, DisplayExt, TextBufferExt, TextBufferExtManual, TextViewExt, WidgetExt,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
@ -73,10 +77,13 @@ impl Code {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply code `Tag` to given `TextBuffer` using `Self.index`
|
/// Apply code `Tag` to given `TextView` using `Self.index`
|
||||||
pub fn render(&mut self, buffer: &TextBuffer) {
|
pub fn render(&mut self, text_view: &TextView) {
|
||||||
|
let buffer = text_view.buffer();
|
||||||
let syntax = Syntax::new();
|
let syntax = Syntax::new();
|
||||||
|
|
||||||
assert!(buffer.tag_table().add(&self.alt));
|
assert!(buffer.tag_table().add(&self.alt));
|
||||||
|
|
||||||
for (k, v) in self.index.iter() {
|
for (k, v) in self.index.iter() {
|
||||||
while let Some((mut m_start, mut m_end)) =
|
while let Some((mut m_start, mut m_end)) =
|
||||||
buffer
|
buffer
|
||||||
|
|
@ -84,24 +91,124 @@ impl Code {
|
||||||
.forward_search(k, TextSearchFlags::VISIBLE_ONLY, None)
|
.forward_search(k, TextSearchFlags::VISIBLE_ONLY, None)
|
||||||
{
|
{
|
||||||
buffer.delete(&mut m_start, &mut m_end);
|
buffer.delete(&mut m_start, &mut m_end);
|
||||||
if let Some(ref alt) = v.alt {
|
text_view.add_child_at_anchor(
|
||||||
buffer.insert_with_tags(&mut m_start, &format!("{alt}\n"), &[&self.alt])
|
&{
|
||||||
}
|
const MARGIN: i32 = 16;
|
||||||
match syntax.highlight(&v.data, v.alt.as_ref()) {
|
let widget = Box::builder()
|
||||||
Ok(highlight) => {
|
.css_classes(["card"])
|
||||||
for (syntax_tag, entity) in highlight {
|
.halign(Align::Fill)
|
||||||
assert!(buffer.tag_table().add(&syntax_tag));
|
.hexpand(true)
|
||||||
buffer.insert_with_tags(&mut m_start, &entity, &[&syntax_tag])
|
.margin_bottom(MARGIN / 2)
|
||||||
}
|
.orientation(Orientation::Vertical)
|
||||||
}
|
.build();
|
||||||
Err(_) => {
|
widget.append(&{
|
||||||
// Try ANSI/SGR format (terminal emulation) @TODO optional
|
let header = Box::builder()
|
||||||
for (syntax_tag, entity) in ansi::format(&v.data) {
|
.halign(Align::Fill)
|
||||||
assert!(buffer.tag_table().add(&syntax_tag));
|
.hexpand(true)
|
||||||
buffer.insert_with_tags(&mut m_start, &entity, &[&syntax_tag])
|
.orientation(Orientation::Horizontal)
|
||||||
}
|
.build();
|
||||||
}
|
if let Some(ref alt) = v.alt {
|
||||||
}
|
header.append(
|
||||||
|
&Label::builder()
|
||||||
|
.halign(Align::Start)
|
||||||
|
.hexpand(true)
|
||||||
|
.label(alt)
|
||||||
|
.margin_bottom(MARGIN)
|
||||||
|
.margin_end(MARGIN)
|
||||||
|
.margin_start(MARGIN)
|
||||||
|
.margin_top(MARGIN)
|
||||||
|
.selectable(true)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
header.append(&{
|
||||||
|
let copy = Button::builder()
|
||||||
|
.css_classes(["circular", "flat"])
|
||||||
|
.halign(Align::End)
|
||||||
|
.icon_name("edit-copy-symbolic")
|
||||||
|
.margin_bottom(MARGIN / 2)
|
||||||
|
.margin_end(MARGIN / 2)
|
||||||
|
.margin_start(MARGIN / 2)
|
||||||
|
.margin_top(MARGIN / 2)
|
||||||
|
.tooltip_text("Copy")
|
||||||
|
.valign(Align::Center)
|
||||||
|
.build();
|
||||||
|
copy.set_cursor_from_name(Some("pointer"));
|
||||||
|
copy.connect_clicked({
|
||||||
|
let source = v.data.clone();
|
||||||
|
move |_| {
|
||||||
|
Display::default().unwrap().clipboard().set_text(&source)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
copy
|
||||||
|
});
|
||||||
|
header
|
||||||
|
});
|
||||||
|
widget.append(
|
||||||
|
&Separator::builder()
|
||||||
|
.orientation(Orientation::Horizontal)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
widget.append(&{
|
||||||
|
ScrolledWindow::builder()
|
||||||
|
.child(
|
||||||
|
&TextView::builder()
|
||||||
|
.buffer(&{
|
||||||
|
let b = TextBuffer::new(Some(&TextTagTable::new()));
|
||||||
|
let mut start = b.start_iter();
|
||||||
|
match syntax.highlight(&v.data, v.alt.as_ref()) {
|
||||||
|
Ok(highlight) => {
|
||||||
|
for (syntax_tag, entity) in highlight {
|
||||||
|
assert!(b.tag_table().add(&syntax_tag));
|
||||||
|
b.insert_with_tags(
|
||||||
|
&mut start,
|
||||||
|
&entity,
|
||||||
|
&[&syntax_tag],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// Try ANSI/SGR format (terminal emulation) @TODO optional
|
||||||
|
for (syntax_tag, entity) in
|
||||||
|
ansi::format(&v.data)
|
||||||
|
{
|
||||||
|
assert!(b.tag_table().add(&syntax_tag));
|
||||||
|
b.insert_with_tags(
|
||||||
|
&mut start,
|
||||||
|
&entity,
|
||||||
|
&[&syntax_tag],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b
|
||||||
|
})
|
||||||
|
.css_classes(["code-block"])
|
||||||
|
.cursor_visible(false)
|
||||||
|
.editable(false)
|
||||||
|
.wrap_mode(WrapMode::None)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.margin_end(MARGIN)
|
||||||
|
.margin_start(MARGIN)
|
||||||
|
.margin_top(MARGIN)
|
||||||
|
.vscrollbar_policy(PolicyType::Never)
|
||||||
|
.hscrollbar_policy(PolicyType::Automatic)
|
||||||
|
.propagate_natural_height(true)
|
||||||
|
.build()
|
||||||
|
});
|
||||||
|
idle_add_local({
|
||||||
|
let widget = widget.clone();
|
||||||
|
let text_view = text_view.clone();
|
||||||
|
move || {
|
||||||
|
widget.set_width_request(text_view.width() - 22);
|
||||||
|
ControlFlow::Break
|
||||||
|
}
|
||||||
|
});
|
||||||
|
widget
|
||||||
|
},
|
||||||
|
&buffer.create_child_anchor(&mut m_end),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ impl Tag {
|
||||||
Self {
|
Self {
|
||||||
text_tag: TextTag::builder()
|
text_tag: TextTag::builder()
|
||||||
.family("monospace") // @TODO
|
.family("monospace") // @TODO
|
||||||
.left_margin(28)
|
|
||||||
.scale(0.81) // * the rounded `0.8` value crops text for some reason @TODO
|
.scale(0.81) // * the rounded `0.8` value crops text for some reason @TODO
|
||||||
.wrap_mode(WrapMode::None)
|
.wrap_mode(WrapMode::None)
|
||||||
.build(),
|
.build(),
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ impl Tag {
|
||||||
Self {
|
Self {
|
||||||
text_tag: TextTag::builder()
|
text_tag: TextTag::builder()
|
||||||
.family("monospace") // @TODO
|
.family("monospace") // @TODO
|
||||||
.left_margin(28)
|
|
||||||
.scale(0.81) // * the rounded `0.8` value crops text for some reason @TODO
|
.scale(0.81) // * the rounded `0.8` value crops text for some reason @TODO
|
||||||
.wrap_mode(WrapMode::None)
|
.wrap_mode(WrapMode::None)
|
||||||
.build(),
|
.build(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue