Skip to content

Commit

Permalink
textbox: wordwrap fixes, optimize scrolling (vlang#392)
Browse files Browse the repository at this point in the history
  • Loading branch information
rcqls authored Oct 4, 2021
1 parent 7976112 commit 7677609
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 30 deletions.
4 changes: 4 additions & 0 deletions examples/demo_scrollview.v
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ fn main() {
height: 600
title: 'V UI: Scrollview'
mode: .resizable
// on_init: fn(win &ui.Window) {
// mut tb := win.textbox('info')
// tb.tv.test_textwidth("abcdefghijklmnrputwxyz &éèdzefzefzef")
// }
children: [
ui.row(
widths: ui.stretch
Expand Down
4 changes: 2 additions & 2 deletions ui_extra_scrollview.v
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,9 @@ fn (mut sv ScrollView) change_value(mode ScrollViewPart) {
// Special treatment for textbox
mut sw := sv.widget
if mut sw is TextBox {
// println("textbox change")
// println("textbox scroll changed")
if sw.has_scrollview {
sw.tv.update_lines()
sw.tv.scroll_changed()
}
}
// User defined treatment for scrollable widget
Expand Down
152 changes: 124 additions & 28 deletions ui_extra_textview.v
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module ui

import gx
// import time
// import encoding.utf8

// position (cursor_pos, sel_start, sel_end) set in the runes world
Expand All @@ -27,28 +28,32 @@ In the process to make lines only dealing with visible lines:
- if <from> is the line starting the visibility and <to> the line ending the visibility
- if lines := (*tv.text).split("\n")
- then:
tvl.lines[0] = lines[..from].join("\n")
tvl.lines[0] = lines[..from_j].join("\n")
tvl.lines[1..(to - from)] = lines[from .. (to + 1)]
tvl.lines[to - from] = lines[(to + 1) ..]
*/
struct TextLinesView {
pub mut:
lines []string
from int
to int
cursor_pos_i int
cursor_pos_j int
sel_start_j int
sel_start_i int
sel_end_i int
sel_end_j int
lines []string
from_i []int
to_i []int
from_j int
to_j int
refresh_visible_lines bool
cursor_pos_i int
cursor_pos_j int
sel_start_j int
sel_start_i int
sel_end_i int
sel_end_j int
}

pub fn (mut tv TextView) init(tb &TextBox) {
tv.tb = tb
tv.text = tb.text // delegate text from tb
tv.update_line_height()
// println('line height: $tv.line_height')
tv.refresh_visible_lines()
tv.update_lines()
tv.cancel_selection()
tv.sync_text_pos()
Expand All @@ -57,8 +62,8 @@ pub fn (mut tv TextView) init(tb &TextBox) {
pub fn (tv &TextView) size() (int, int) {
tv.load_current_style()
mut w, mut h := 0, textbox_padding_y * 2 + tv.line_height * tv.tlv.lines.len
// println("size $tv.tb.id: $tv.tlv.lines $tv.tlv.lines.len $tv.tlv.to")
for line in tv.tlv.lines[tv.tlv.from..(tv.tlv.to + 1)] {
// println("size $tv.tb.id: $tv.tlv.lines $tv.tlv.lines.len $tv.tlv.to_j")
for line in tv.tlv.lines[tv.tlv.from_j..(tv.tlv.to_j + 1)] {
lw := tv.text_width(line)
if lw > w {
w = lw
Expand All @@ -80,6 +85,7 @@ pub fn (mut tv TextView) is_wordwrap() bool {
pub fn (mut tv TextView) set_wordwrap(state bool) {
tv.tb.is_wordwrap = state
tv.sync_text_pos()
tv.refresh_visible_lines()
tv.update_lines()
tv.sync_text_lines()
}
Expand Down Expand Up @@ -152,11 +158,35 @@ pub fn (mut tv TextView) visible_lines() {
j2 = tv.tb.height / tv.line_height
}
jmax := tv.tlv.lines.len - 1
// println("j1 $j1 $jmax")
if j1 > jmax {
j1 = jmax
}
// println("j2 $j2 $jmax")
if j2 > jmax {
j2 = jmax
}
tv.tlv.from, tv.tlv.to = j1, j2
tv.tlv.from_j, tv.tlv.to_j = j1, j2
tv.update_all_visible_lines()
}

fn (mut tv TextView) refresh_visible_lines() {
tv.tlv.refresh_visible_lines = true
}

fn (mut tv TextView) update_all_visible_lines() {
if tv.tlv.refresh_visible_lines {
// println("visible line ${time.now()}")
tv.tlv.from_i.clear()
tv.tlv.to_i.clear()
for j in tv.tlv.from_j .. tv.tlv.to_j + 1 {
tv.tlv.from_i << tv.text_pos_from_x(tv.tlv.lines[j], tv.tb.scrollview.offset_x)
tv.tlv.to_i << tv.text_pos_from_x(tv.tlv.lines[j], tv.tb.scrollview.offset_x +
tv.tb.width)
}
// refresh_visible_lines done
tv.tlv.refresh_visible_lines = false
}
}

pub fn (mut tv TextView) update_lines() {
Expand All @@ -176,8 +206,15 @@ pub fn (mut tv TextView) update_lines() {
}
}

fn (mut tv TextView) scroll_changed() {
// println("textbox scroll changed ${time.now()}")
tv.refresh_visible_lines()
tv.update_lines()
}

fn (mut tv TextView) draw_textlines() {
if tv.tb.is_sync {
tv.refresh_visible_lines()
tv.update_lines()
}

Expand All @@ -186,14 +223,12 @@ fn (mut tv TextView) draw_textlines() {

// draw only visible text lines
tv.load_current_style()
x, mut y := tv.tb.x + textbox_padding_x, tv.tb.y + textbox_padding_y
mut y := tv.tb.y + textbox_padding_y
if tv.tb.has_scrollview {
y += (tv.tlv.from) * tv.line_height
y += (tv.tlv.from_j) * tv.line_height
}
for line in tv.tlv.lines[tv.tlv.from..(tv.tlv.to + 1)] {
// println(line)
// draw_text(tv.tb, x, y, line)
tv.draw_text(x, y, line)
for i, line in tv.tlv.lines[tv.tlv.from_j..(tv.tlv.to_j + 1)] {
tv.draw_visible_line(i, y, line)
y += tv.line_height
}

Expand All @@ -203,6 +238,19 @@ fn (mut tv TextView) draw_textlines() {
}
}

pub fn (mut tv TextView) draw_visible_line(i int, y int, text string) {
if i == tv.tlv.from_i.len {
// println("draw_visible_line $i $tv.tlv.from_i.len")
tv.refresh_visible_lines()
tv.visible_lines()
}
imin, imax := tv.tlv.from_i[i], tv.tlv.to_i[i]
ustr := text.runes()
// println("draw visible $imin, $imax $ustr")
tv.draw_text(tv.tb.x + textbox_padding_x + tv.text_width(ustr[0..imin].string()),
y, ustr[imin..imax].string())
}

fn (mut tv TextView) draw_selection() {
if !tv.is_sel_active() {
// println("return draw_sel")
Expand Down Expand Up @@ -247,6 +295,7 @@ fn (mut tv TextView) insert(s string) {
unsafe {
*tv.text = ustr.string()
}
tv.refresh_visible_lines()
tv.update_lines()
}

Expand All @@ -256,6 +305,7 @@ fn (mut tv TextView) delete_cur_char() {
unsafe {
*tv.text = ustr.string()
}
tv.refresh_visible_lines()
tv.update_lines()
}

Expand All @@ -270,6 +320,7 @@ fn (mut tv TextView) delete_prev_char() {
unsafe {
*tv.text = ustr.string()
}
tv.refresh_visible_lines()
tv.update_lines()
}

Expand All @@ -285,6 +336,7 @@ fn (mut tv TextView) delete_selection() {
unsafe {
*tv.text = ustr.string()
}
tv.refresh_visible_lines()
tv.update_lines()
mut tb := tv.tb
// Only if scrollview become inactive, reset the scrollview
Expand Down Expand Up @@ -410,9 +462,9 @@ pub fn (mut tv TextView) cursor_allways_visible() {
return
}
// vertically
if tv.tlv.cursor_pos_j <= tv.tlv.from {
if tv.tlv.cursor_pos_j <= tv.tlv.from_j {
tv.scroll_y_to_cursor(false)
} else if tv.tlv.cursor_pos_j >= tv.tlv.to {
} else if tv.tlv.cursor_pos_j >= tv.tlv.to_j {
tv.scroll_y_to_cursor(true)
}
// horizontally
Expand Down Expand Up @@ -692,7 +744,7 @@ pub fn (mut tv TextView) do_logview(cfg LogViewConfig) {
println("Warning: use of task do_logview requires textbox to have 'scrollview: true'")
return
}
if tv.tlv.to + cfg.nb_lines > tv.tlv.lines.len {
if tv.tlv.to_j + cfg.nb_lines > tv.tlv.lines.len {
tv.scroll_y_to_end()
}
}
Expand Down Expand Up @@ -749,9 +801,9 @@ pub fn (mut tv TextView) scroll_y_to_end() {
fn (mut tv TextView) word_wrap_text() {
lines := (*tv.text).split('\n')
mut word_wrapped_lines := []string{}
// println('word_wrap_text: $tv.tlv.from -> $tv.tlv.to')
for j, line in lines {
ww_lines := if j < tv.tlv.from || j > tv.tlv.to { [line] } else { tv.word_wrap_line(line) }
// println('word_wrap_text: $tv.tlv.from_j -> $tv.tlv.to_j')
for line in lines {
ww_lines := tv.word_wrap_line(line)
word_wrapped_lines << ww_lines
}
// println('tl: $lines \n $word_wrapped_lines.len $word_wrapped_lines')
Expand Down Expand Up @@ -875,18 +927,45 @@ pub fn (tv &TextView) text_pos_from_x(text string, x int) int {
return 0
}
tv.load_current_style()
mut prev_width := 0
mut prev_width := 0.0
ustr := text.runes()
mut width, mut width_cur := 0.0, 0.0
for i in 0 .. ustr.len {
width += width_cur
// if width != tv.text_width(ustr[..i].string()) {
// // println("widthhhh $i $width ${tv.text_width(ustr[..i].string())}")
// }
width_cur = tv.text_width_additive(ustr[i..(i + 1)].string())
width2 := if i < ustr.len { width + width_cur } else { width }
if (prev_width + width) / 2 <= x && x <= (width + width2) / 2 {
return i
}
prev_width = width
}
return ustr.len
}

/*
THIS OLD VERSION LASTS VERY LONG
pub fn (tv &TextView) text_pos_from_x(text string, x int) int {
if x <= 0 {
return 0
}
tv.load_current_style()
mut prev_width, mut tmp := 0, 0
ustr := text.runes()
for i in 0 .. ustr.len {
width := tv.text_width(ustr[..i].string())
width2 := if i < ustr.len { tv.text_width(ustr[..(i + 1)].string()) } else { width }
width := tmp
tmp = tv.text_width(ustr[..(i + 1)].string())
width2 := if i < ustr.len { tmp } else { width }
if (prev_width + width) / 2 <= x && x <= (width + width2) / 2 {
return i
}
prev_width = width
}
return ustr.len
}
*/

// TextStyles
fn (tv &TextView) draw_text(x int, y int, text string) {
Expand All @@ -897,6 +976,11 @@ fn (tv &TextView) text_width(text string) int {
return DrawTextWidget(tv.tb).text_width(text)
}

// Added to have mostly additive text width function
fn (tv &TextView) text_width_additive(text string) f64 {
return DrawTextWidget(tv.tb).text_width_additive(text)
}

fn (tv &TextView) text_height(text string) int {
return DrawTextWidget(tv.tb).text_width(text)
}
Expand All @@ -919,3 +1003,15 @@ fn (tv &TextView) update_text_style(ts TextStyleConfig) {
fn (tv &TextView) load_current_style() {
DrawTextWidget(tv.tb).load_current_style()
}

// that's weird text_width is not additive function
pub fn (tv &TextView) test_textwidth(text string) {
ustr := text.runes()
mut width := 0.0
for i in 0 .. ustr.len {
tmp := DrawTextWidget(tv.tb).text_width_additive(ustr[i..(i + 1)].string())
width += tmp
tmp2, _ := tv.text_size(ustr[..i + 1].string())
println('$i) ${ustr[i..(i + 1)].string()} ($tmp) ${ustr[..i + 1].string()} ${width + 1.0} == $tmp2')
}
}
6 changes: 6 additions & 0 deletions ui_interface_drawtext.v
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ pub fn (w DrawTextWidget) text_width(text string) int {
return w.ui.gg.text_width(text)
}

pub fn (w DrawTextWidget) text_width_additive(text string) f64 {
ctx := w.ui.gg
adv := C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(text.str), 0, 0)
return adv / ctx.scale
}

pub fn (w DrawTextWidget) text_height(text string) int {
return w.ui.gg.text_height(text)
}
Expand Down

0 comments on commit 7677609

Please sign in to comment.