Implement text clicking again
This commit is contained in:
parent
a66b8fdde2
commit
a867d4520a
1 changed files with 99 additions and 25 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
use orbclient::{Color, EventOption, Renderer, Window, WindowFlag};
|
use orbclient::{Color, EventOption, Renderer, Window, WindowFlag};
|
||||||
use std::{
|
use std::{
|
||||||
cmp,
|
cmp,
|
||||||
|
env,
|
||||||
|
fs,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -21,7 +23,8 @@ impl<'a> Font<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FontLayoutGlyph<'a> {
|
struct FontLayoutGlyph<'a> {
|
||||||
info: rustybuzz::GlyphInfo,
|
start: usize,
|
||||||
|
end: usize,
|
||||||
inner: rusttype::PositionedGlyph<'a>,
|
inner: rusttype::PositionedGlyph<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,6 +56,8 @@ impl<'a> FontLayoutLine<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FontShapeGlyph<'a> {
|
struct FontShapeGlyph<'a> {
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
info: rustybuzz::GlyphInfo,
|
info: rustybuzz::GlyphInfo,
|
||||||
pos: rustybuzz::GlyphPosition,
|
pos: rustybuzz::GlyphPosition,
|
||||||
font: &'a Font<'a>
|
font: &'a Font<'a>
|
||||||
|
|
@ -125,7 +130,8 @@ impl<'a> FontShapeLine<'a> {
|
||||||
));
|
));
|
||||||
|
|
||||||
glyphs.push(FontLayoutGlyph {
|
glyphs.push(FontLayoutGlyph {
|
||||||
info: glyph.info,
|
start: glyph.start,
|
||||||
|
end: glyph.end,
|
||||||
inner,
|
inner,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -149,16 +155,18 @@ struct FontMatches<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FontMatches<'a> {
|
impl<'a> FontMatches<'a> {
|
||||||
pub fn shape_span(&self, string: &str) -> FontShapeSpan {
|
fn shape_span(&self, string: &'a str, start_span: usize, end_span: usize) -> FontShapeSpan {
|
||||||
|
let span = &string[start_span..end_span];
|
||||||
|
|
||||||
let mut spans_by_font = Vec::with_capacity(self.fonts.len());
|
let mut spans_by_font = Vec::with_capacity(self.fonts.len());
|
||||||
for (font_i, font) in self.fonts.iter().enumerate() {
|
for (font_i, font) in self.fonts.iter().enumerate() {
|
||||||
let mut buffer = rustybuzz::UnicodeBuffer::new();
|
let mut buffer = rustybuzz::UnicodeBuffer::new();
|
||||||
buffer.push_str(string);
|
buffer.push_str(span);
|
||||||
buffer.guess_segment_properties();
|
buffer.guess_segment_properties();
|
||||||
let script = buffer.script();
|
let script = buffer.script();
|
||||||
let direction = buffer.direction();
|
let direction = buffer.direction();
|
||||||
if font_i == 0 {
|
if font_i == 0 {
|
||||||
println!(" {:?}, {:?}: '{}'", script, direction, string);
|
println!("{:?}, {:?}: '{}'", script, direction, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
let glyph_buffer = rustybuzz::shape(&font.rustybuzz, &[], buffer);
|
let glyph_buffer = rustybuzz::shape(&font.rustybuzz, &[], buffer);
|
||||||
|
|
@ -173,12 +181,21 @@ impl<'a> FontMatches<'a> {
|
||||||
misses += 1;
|
misses += 1;
|
||||||
}
|
}
|
||||||
glyphs.push(FontShapeGlyph {
|
glyphs.push(FontShapeGlyph {
|
||||||
|
start: start_span + info.cluster as usize,
|
||||||
|
end: end_span, // Set later
|
||||||
info: *info,
|
info: *info,
|
||||||
pos: *pos,
|
pos: *pos,
|
||||||
font,
|
font,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust end of glyphs
|
||||||
|
for i in (1..glyphs.len()).rev() {
|
||||||
|
if glyphs[i - 1].start == glyphs[i].start {
|
||||||
|
glyphs[i - 1].end = glyphs[i].end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let span = FontShapeSpan {
|
let span = FontShapeSpan {
|
||||||
direction,
|
direction,
|
||||||
glyphs
|
glyphs
|
||||||
|
|
@ -206,26 +223,25 @@ impl<'a> FontMatches<'a> {
|
||||||
spans_by_font.remove(least_misses_i).1
|
spans_by_font.remove(least_misses_i).1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shape_line(&self, string: &str) -> FontShapeLine {
|
fn shape_line(&self, string: &'a str, start_line: usize, end_line: usize) -> FontShapeLine {
|
||||||
use unicode_script::{Script, UnicodeScript};
|
use unicode_script::{Script, UnicodeScript};
|
||||||
|
|
||||||
|
let line = &string[start_line..end_line];
|
||||||
|
|
||||||
//TODO: more special handling of characters
|
//TODO: more special handling of characters
|
||||||
let mut spans = Vec::new();
|
let mut spans = Vec::new();
|
||||||
|
|
||||||
println!("'{}'", string);
|
|
||||||
|
|
||||||
let mut start = 0;
|
let mut start = 0;
|
||||||
let mut prev = Script::Unknown;
|
let mut prev = Script::Unknown;
|
||||||
for (i, c) in string.char_indices() {
|
for (i, c) in line.char_indices() {
|
||||||
if ! string.is_char_boundary(i) {
|
if ! line.is_char_boundary(i) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cur = c.script();
|
let cur = c.script();
|
||||||
if prev != cur && prev != Script::Unknown {
|
if prev != cur && prev != Script::Unknown {
|
||||||
println!("*{:?} != {:?}", prev, cur);
|
|
||||||
// No combination, start new span
|
// No combination, start new span
|
||||||
spans.push(self.shape_span(&string[start..i]));
|
spans.push(self.shape_span(string, start_line + start, start_line + i));
|
||||||
start = i;
|
start = i;
|
||||||
prev = Script::Unknown;
|
prev = Script::Unknown;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -233,9 +249,9 @@ impl<'a> FontMatches<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spans.push(self.shape_span(&string[start..string.len()]));
|
spans.push(self.shape_span(string, start_line + start, start_line + line.len()));
|
||||||
|
|
||||||
let bidi = unicode_bidi::BidiInfo::new(string, None);
|
let bidi = unicode_bidi::BidiInfo::new(line, None);
|
||||||
let rtl = if bidi.paragraphs.is_empty() {
|
let rtl = if bidi.paragraphs.is_empty() {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -248,6 +264,26 @@ impl<'a> FontMatches<'a> {
|
||||||
spans,
|
spans,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn shape(&self, string: &'a str) -> Vec<FontShapeLine> {
|
||||||
|
let mut lines = Vec::new();
|
||||||
|
|
||||||
|
let mut start = 0;
|
||||||
|
for (i, c) in string.char_indices() {
|
||||||
|
if ! string.is_char_boundary(i) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == '\n' {
|
||||||
|
lines.push(self.shape_line(string, start, i));
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push(self.shape_line(string, start, string.len()));
|
||||||
|
|
||||||
|
lines
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FontSystem<'a> {
|
struct FontSystem<'a> {
|
||||||
|
|
@ -319,9 +355,15 @@ fn main() {
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
#[cfg(feature = "mono")]
|
#[cfg(feature = "mono")]
|
||||||
let text = include_str!("../res/mono.txt");
|
let default_text = include_str!("../res/mono.txt");
|
||||||
#[cfg(not(feature = "mono"))]
|
#[cfg(not(feature = "mono"))]
|
||||||
let text = include_str!("../res/proportional.txt");
|
let default_text = include_str!("../res/proportional.txt");
|
||||||
|
|
||||||
|
let text = if let Some(arg) = env::args().nth(1) {
|
||||||
|
fs::read_to_string(&arg).expect("failed to open file")
|
||||||
|
} else {
|
||||||
|
default_text.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
let bg_color = Color::rgb(0x34, 0x34, 0x34);
|
let bg_color = Color::rgb(0x34, 0x34, 0x34);
|
||||||
let font_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
let font_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
||||||
|
|
@ -360,14 +402,12 @@ fn main() {
|
||||||
|
|
||||||
let font_matches = font_system.matches(font_pattern).unwrap();
|
let font_matches = font_system.matches(font_pattern).unwrap();
|
||||||
|
|
||||||
let mut shape_lines = Vec::new();
|
let shape_lines = font_matches.shape(&text);
|
||||||
for line in text.lines() {
|
|
||||||
shape_lines.push(font_matches.shape_line(line));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut layout_lines = Vec::new();
|
let mut layout_lines = Vec::new();
|
||||||
let mut mouse_x = -1;
|
let mut mouse_x = -1;
|
||||||
let mut mouse_y = -1;
|
let mut mouse_y = -1;
|
||||||
|
let mut mouse_left = false;
|
||||||
let mut redraw = true;
|
let mut redraw = true;
|
||||||
let mut relayout = true;
|
let mut relayout = true;
|
||||||
let mut scroll = 0;
|
let mut scroll = 0;
|
||||||
|
|
@ -379,7 +419,8 @@ fn main() {
|
||||||
|
|
||||||
layout_lines.clear();
|
layout_lines.clear();
|
||||||
for line in shape_lines.iter() {
|
for line in shape_lines.iter() {
|
||||||
line.layout(font_size, window.width() as i32, &mut layout_lines);
|
let line_width = window.width() as i32 - 16;
|
||||||
|
line.layout(font_size, line_width, &mut layout_lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
redraw = true;
|
redraw = true;
|
||||||
|
|
@ -400,17 +441,40 @@ fn main() {
|
||||||
scroll
|
scroll
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let line_x = 8;
|
||||||
let mut line_y = line_height;
|
let mut line_y = line_height;
|
||||||
for line in layout_lines.iter().skip(scroll as usize) {
|
for line in layout_lines.iter().skip(scroll as usize) {
|
||||||
if line_y >= window.height() as i32 {
|
if line_y >= window.height() as i32 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut line_x = 0;
|
if mouse_y >= line_y - font_size
|
||||||
let line_width = window.width() as i32;
|
&& mouse_y < line_y - font_size + line_height
|
||||||
|
{
|
||||||
|
let mut i = 0;
|
||||||
|
while i < line.glyphs.len() {
|
||||||
|
let glyph = &line.glyphs[i];
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
if let Some(bb) = glyph.inner.pixel_bounding_box() {
|
||||||
|
if mouse_x >= line_x + bb.min.x && mouse_x <= line_x + bb.max.x {
|
||||||
|
window.rect(
|
||||||
|
line_x + bb.min.x,
|
||||||
|
line_y - font_size,
|
||||||
|
bb.width() as u32,
|
||||||
|
line_height as u32,
|
||||||
|
Color::rgba(0xFF, 0xFF, 0xFF, 0x20)
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("{}, {}: '{}'", glyph.start, glyph.end, &text[glyph.start..glyph.end]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
line.draw(
|
line.draw(
|
||||||
&mut window,
|
&mut window,
|
||||||
0,
|
line_x,
|
||||||
line_y,
|
line_y,
|
||||||
font_color
|
font_color
|
||||||
);
|
);
|
||||||
|
|
@ -448,8 +512,18 @@ fn main() {
|
||||||
EventOption::Mouse(event) => {
|
EventOption::Mouse(event) => {
|
||||||
mouse_x = event.x;
|
mouse_x = event.x;
|
||||||
mouse_y = event.y;
|
mouse_y = event.y;
|
||||||
redraw = true;
|
if mouse_left {
|
||||||
|
redraw = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
EventOption::Button(event) => {
|
||||||
|
if event.left != mouse_left {
|
||||||
|
mouse_left = event.left;
|
||||||
|
if mouse_left {
|
||||||
|
redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
EventOption::Resize(_) => {
|
EventOption::Resize(_) => {
|
||||||
relayout = true;
|
relayout = true;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue