use super::*;
#[derive(Clone, Debug)]
pub struct TextSurfaceLine {
pub start: usize,
pub end: usize,
pub rect: [i32; 4],
pub carets: Vec<(usize, i32)>,
}
impl TextSurfaceLine {
fn x(&self, at: usize) -> i32 {
self.carets
.iter()
.rev()
.find(|(offset, _)| *offset <= at)
.map_or(self.rect[0], |(_, x)| *x)
}
fn hit(&self, x: i32) -> usize {
self.carets
.iter()
.min_by_key(|(_, px)| (i64::from(*px) - i64::from(x)).abs())
.map_or(self.start, |(offset, _)| *offset)
}
}
impl Ui {
#[cfg_attr(jfgui_dev, track_caller)]
pub fn textbox_surface(
&mut self,
id: u32,
text: &mut String,
cap_flags: u32,
lines: &[TextSurfaceLine],
) -> u32 {
let _src = self.src_scope();
self.m.edit.surface_change = None;
self.m.edit.surface_start = None;
if lines.is_empty()
|| lines.len() > 100000
|| lines.iter().any(|line| {
line.start > line.end
|| !text.is_char_boundary(line.start)
|| !text.is_char_boundary(line.end)
|| line.rect[2] < 1
|| line.rect[3] < 1
|| line.carets.is_empty()
|| line.carets.iter().any(|(at, _)| {
*at < line.start || *at > line.end || !text.is_char_boundary(*at)
})
|| line.carets.first().map(|(at, _)| *at) != Some(line.start)
|| line.carets.last().map(|(at, _)| *at) != Some(line.end)
|| line.carets.windows(2).any(|p| p[0].0 >= p[1].0)
|| line.carets.len() != text[line.start..line.end].chars().count() + 1
})
|| lines.windows(2).any(|p| p[0].end > p[1].start)
{
return 0;
}
let original = text.clone();
let mut bytes = core::mem::take(text).into_bytes();
core::mem::swap(&mut bytes, &mut self.m.edit.buf);
let prior = self.m.edit.surface.replace(lines.to_vec());
let flags = self.tb_start(id, cap_flags | TB_MULTI, TB_FLAG_MASK & !TB_PASSWORD);
let mut ev = 0;
let hb = HB_FOCUS
| HB_TEXT
| CUR_IBEAM << HB_CURSOR_SHIFT
| if flags & ST_DISABLED != 0 {
HB_DISABLED
} else {
0
};
let mut registered = false;
for line in lines {
let r = line.rect;
let before = self.wp.ctx.hits[self.wp.ctx.hit_cur].len();
ev |= self.hit_rect(id, r[0], r[1], r[2], r[3], hb);
if let Some(hit) = self.wp.ctx.hits[self.wp.ctx.hit_cur].get_mut(before) {
hit.continuation = registered;
registered = true;
}
}
let mut editing = self.tb_focus();
if editing {
self.m.edit.ed_multi = true;
self.m.edit.ml_page = 20;
let c = &self.wp.ctx;
if c.pressed & (1 << BTN_LEFT) != 0 && ev & EV_HOVER != 0 && c.active == id {
let at = self.surface_hit(self.ltr_x(c.mx), c.my);
self.m.edit.ed_prefx = -1;
self.tb_press(ev, at);
} else if ev & EV_HELD != 0 && self.m.edit.ed_drag {
let at = self.surface_hit(self.ltr_x(c.mx), c.my);
self.tb_drag_to(at);
} else {
self.m.edit.ed_drag = false;
}
self.tb_keys();
self.tb_typed();
if self.m.edit.tb_chg {
self.m.edit.tb_ev |= EV_ACT;
self.repass();
}
editing = self.m.edit.ed_id == id;
}
if editing && !self.m.edit.tb_chg {
let (a, b) = self.tb_sel();
for line in lines {
let lo = a.max(line.start);
let hi = b.min(line.end);
if lo < hi || (lo == hi && a <= line.end && b > line.end) {
let x = line.x(lo);
let end = line
.x(hi)
.saturating_add(if b > line.end { self.dp(4) } else { 0 });
self.draw_color((self.color(T_ACCENT) & 0x00ffffff) | 0x50000000);
self.draw_rect(x, line.rect[1], (end - x).max(1), line.rect[3]);
}
}
let line = &lines[self.surface_line(self.m.edit.ed_caret)];
let x = line.x(self.m.edit.ed_caret);
if self.tb_caret_on() {
self.draw_color(0xff20272b);
self.draw_rect(x, line.rect[1], self.hair(), line.rect[3]);
}
self.ime_pos(x, line.rect[1], line.rect[3]);
}
self.wp.ctx.last_rect = lines[if editing {
self.surface_line(self.m.edit.ed_caret)
} else {
0
}]
.rect;
let result = self.tb_finish(ev, editing);
self.m.edit.surface = prior;
core::mem::swap(&mut bytes, &mut self.m.edit.buf);
*text = to_string(bytes);
if original != *text {
let start = self
.m
.edit
.surface_start
.unwrap_or(0)
.min(original.len())
.min(text.len());
let start = Self::tb_fix_index(original.as_bytes(), start);
let mut suffix = original.as_bytes()[start..]
.iter()
.rev()
.zip(text.as_bytes()[start..].iter().rev())
.take_while(|(a, b)| a == b)
.count();
while !original.is_char_boundary(original.len() - suffix)
|| !text.is_char_boundary(text.len() - suffix)
{
suffix -= 1;
}
self.m.edit.surface_change = Some((
start,
original.len() - suffix,
String::from(&text[start..text.len() - suffix]),
));
}
result
}
pub fn textbox_surface_change(&self) -> Option<(usize, usize, String)> {
self.m.edit.surface_change.clone()
}
fn surface_line(&self, offset: usize) -> usize {
self.m
.edit
.surface
.as_ref()
.unwrap()
.iter()
.rposition(|l| l.start <= offset)
.unwrap_or(0)
}
fn surface_hit(&self, x: i32, y: i32) -> usize {
let lines = self.m.edit.surface.as_ref().unwrap();
let line = lines
.iter()
.min_by_key(|line| {
let top = i64::from(line.rect[1]);
let end = top + i64::from(line.rect[3]);
if i64::from(y) < top {
top - i64::from(y)
} else {
(i64::from(y) - end).max(0)
}
})
.unwrap();
Self::tb_fix_index(&self.m.edit.buf, line.hit(x))
}
pub(super) fn surface_vmove(&mut self, delta: i32, shift: bool) {
let lines = self.m.edit.surface.as_ref().unwrap();
let current = self.surface_line(self.m.edit.ed_caret);
let x = if self.m.edit.ed_prefx < 0 {
lines[current].x(self.m.edit.ed_caret)
} else {
self.m.edit.ed_prefx
};
let next = current as i64 + i64::from(delta);
let at = if next < 0 {
0
} else if next >= lines.len() as i64 {
self.m.edit.buf.len()
} else {
Self::tb_fix_index(&self.m.edit.buf, lines[next as usize].hit(x))
};
self.tb_move(at, shift);
self.m.edit.ed_prefx = x;
}
}