fix(editor): fix cursor positioning, scrolling, and clipping
- Fix cursor vertical alignment and ensure it respects ScrollOffset. - Fix scrolling in editor page by correctly registering scroll interactions in the renderer. - Ensure cursor is clipped to the editor text area to prevent drawing over status bars.
This commit is contained in:
parent
a5d1c4bee6
commit
d33e68ab2c
|
|
@ -374,13 +374,15 @@ func EditorLayout(screenWidth, screenHeight ui.Dp, wordWrap bool) []ui.Element {
|
||||||
// approximation until GlyphLayout-based line tracking is added).
|
// approximation until GlyphLayout-based line tracking is added).
|
||||||
line, col := byteOffsetToLineCol(TheState.Editor.Buffer, TheState.Editor.CursorPosition)
|
line, col := byteOffsetToLineCol(TheState.Editor.Buffer, TheState.Editor.CursorPosition)
|
||||||
|
|
||||||
// Create the cursor element at the computed position
|
// Create the cursor element at the computed position.
|
||||||
|
// Ensure the cursor element knows its bounds for clipping.
|
||||||
cursorElem := ui.NewCursor(
|
cursorElem := ui.NewCursor(
|
||||||
"editor_cursor",
|
"editor_cursor",
|
||||||
ui.Region{X: cursorX, Y: cursorY, W: ui.Dp(2), H: lineHeight},
|
ui.Region{X: cursorX, Y: cursorY, W: ui.Dp(2), H: lineHeight},
|
||||||
line, col,
|
line, col,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
cursorElem.ClipRegion = editorRegion
|
||||||
|
|
||||||
return []ui.Element{statusBar, editorElem, cursorElem, bottomBar}
|
return []ui.Element{statusBar, editorElem, cursorElem, bottomBar}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -480,6 +480,7 @@ func NewSearchBar(region Region, query string, match, total int, forward bool) S
|
||||||
type Cursor struct {
|
type Cursor struct {
|
||||||
id string
|
id string
|
||||||
region Region
|
region Region
|
||||||
|
ClipRegion Region // Region to clip drawing
|
||||||
visible bool
|
visible bool
|
||||||
interactions []Interaction
|
interactions []Interaction
|
||||||
Line int
|
Line int
|
||||||
|
|
@ -488,6 +489,7 @@ type Cursor struct {
|
||||||
Selection *Selection
|
Selection *Selection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Cursor) NeedsClip() bool { return true }
|
||||||
func (c Cursor) Type() string { return "cursor" }
|
func (c Cursor) Type() string { return "cursor" }
|
||||||
func (c Cursor) Region() Region { return c.region }
|
func (c Cursor) Region() Region { return c.region }
|
||||||
func (c Cursor) Visible() bool { return c.visible }
|
func (c Cursor) Visible() bool { return c.visible }
|
||||||
|
|
@ -499,6 +501,17 @@ func (c Cursor) String() string {
|
||||||
return fmt.Sprintf("Cursor[%s] region=%+v line=%d col=%d", c.id, c.region, c.Line, c.Column)
|
return fmt.Sprintf("Cursor[%s] region=%+v line=%d col=%d", c.id, c.region, c.Line, c.Column)
|
||||||
}
|
}
|
||||||
func (c Cursor) Draw(gtx layout.Context, r *Renderer) {
|
func (c Cursor) Draw(gtx layout.Context, r *Renderer) {
|
||||||
|
// Clip to the cursor's allocated clip region (e.g., the editor text field)
|
||||||
|
// to prevent drawing over status bars.
|
||||||
|
var stack *clip.Stack
|
||||||
|
if c.ClipRegion.W > 0 && c.ClipRegion.H > 0 {
|
||||||
|
stack = new(clip.Stack)
|
||||||
|
*stack = clip.Rect{
|
||||||
|
Min: image.Point{X: int(r.toPx(c.ClipRegion.X)), Y: int(r.toPx(c.ClipRegion.Y))},
|
||||||
|
Max: image.Point{X: int(r.toPx(c.ClipRegion.X + c.ClipRegion.W)), Y: int(r.toPx(c.ClipRegion.Y + c.ClipRegion.H))},
|
||||||
|
}.Op().Push(gtx.Ops)
|
||||||
|
}
|
||||||
|
|
||||||
// Draw a thin vertical bar (e.g., width 2dp, height 18dp) at the cursor's top-left position
|
// Draw a thin vertical bar (e.g., width 2dp, height 18dp) at the cursor's top-left position
|
||||||
// instead of filling the entire region, which covers the text editor.
|
// instead of filling the entire region, which covers the text editor.
|
||||||
cursorRegion := Region{
|
cursorRegion := Region{
|
||||||
|
|
@ -508,6 +521,10 @@ func (c Cursor) Draw(gtx layout.Context, r *Renderer) {
|
||||||
H: Dp(18),
|
H: Dp(18),
|
||||||
}
|
}
|
||||||
r.drawBg(gtx, cursorRegion, Color{R: 0, G: 0, B: 0, A: 255})
|
r.drawBg(gtx, cursorRegion, Color{R: 0, G: 0, B: 0, A: 255})
|
||||||
|
|
||||||
|
if stack != nil {
|
||||||
|
stack.Pop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCursor(id string, region Region, line, col int, blinking bool) Cursor {
|
func NewCursor(id string, region Region, line, col int, blinking bool) Cursor {
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,15 @@ func (r *Renderer) drawElement(gtx layout.Context, e Element) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, interaction := range interactive.Interactions() {
|
for _, interaction := range interactive.Interactions() {
|
||||||
|
if interaction.Gesture == Scroll {
|
||||||
|
reg, ok := r.scrolls[interactive.ID()]
|
||||||
|
if !ok {
|
||||||
|
reg = scrollReg{scroll: &gesture.Scroll{}}
|
||||||
|
}
|
||||||
|
reg.scroll.Add(gtx.Ops)
|
||||||
|
reg.handler = interaction.Handler
|
||||||
|
r.scrolls[interactive.ID()] = reg
|
||||||
|
}
|
||||||
if interaction.Gesture == Tap {
|
if interaction.Gesture == Tap {
|
||||||
reg, ok := r.clicks[interactive.ID()]
|
reg, ok := r.clicks[interactive.ID()]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user