Added a few context menus
This commit is contained in:
parent
f88268bb0e
commit
6622799019
@ -40,7 +40,6 @@ func (window *window) handleExpose (
|
||||
event xevent.ExposeEvent,
|
||||
) {
|
||||
_, region := window.compressExpose(*event.ExposeEvent)
|
||||
window.system.afterEvent()
|
||||
window.pushRegion(region)
|
||||
}
|
||||
|
||||
@ -79,8 +78,6 @@ func (window *window) handleConfigureNotify (
|
||||
window.child.Invalidate()
|
||||
window.child.InvalidateLayout()
|
||||
}
|
||||
|
||||
window.system.afterEvent()
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,8 +137,6 @@ func (window *window) handleKeyPress (
|
||||
focused, ok := window.focused.element.(tomo.KeyboardTarget)
|
||||
if ok { focused.HandleKeyDown(key, modifiers) }
|
||||
}
|
||||
|
||||
window.system.afterEvent()
|
||||
}
|
||||
|
||||
func (window *window) handleKeyRelease (
|
||||
@ -174,8 +169,6 @@ func (window *window) handleKeyRelease (
|
||||
if window.focused != nil {
|
||||
focused, ok := window.focused.element.(tomo.KeyboardTarget)
|
||||
if ok { focused.HandleKeyUp(key, modifiers) }
|
||||
|
||||
window.system.afterEvent()
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,8 +213,6 @@ func (window *window) handleButtonPress (
|
||||
}
|
||||
underneath.forMouseTargetContainers(callback)
|
||||
}
|
||||
|
||||
window.system.afterEvent()
|
||||
}
|
||||
|
||||
func (window *window) handleButtonRelease (
|
||||
@ -252,8 +243,6 @@ func (window *window) handleButtonRelease (
|
||||
}
|
||||
dragging.forMouseTargetContainers(callback)
|
||||
}
|
||||
|
||||
window.system.afterEvent()
|
||||
}
|
||||
|
||||
func (window *window) handleMotionNotify (
|
||||
@ -279,8 +268,6 @@ func (window *window) handleMotionNotify (
|
||||
child.HandleMotion(image.Pt(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
window.system.afterEvent()
|
||||
}
|
||||
|
||||
func (window *window) handleSelectionNotify (
|
||||
|
@ -67,12 +67,12 @@ func (backend *Backend) Run () (err error) {
|
||||
<- pingAfter
|
||||
case callback := <- backend.doChannel:
|
||||
callback()
|
||||
for _, window := range backend.windows {
|
||||
window.system.afterEvent()
|
||||
}
|
||||
case <- pingQuit:
|
||||
return
|
||||
}
|
||||
for _, window := range backend.windows {
|
||||
window.system.afterEvent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
package elements
|
||||
|
||||
import "image"
|
||||
import "golang.org/x/image/math/fixed"
|
||||
import "git.tebibyte.media/sashakoshka/tomo"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/data"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/input"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/textdraw"
|
||||
import "git.tebibyte.media/sashakoshka/tomo/default/theme"
|
||||
@ -48,6 +51,32 @@ func (element *Label) Entity () tomo.Entity {
|
||||
return element.entity
|
||||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Label) Draw (destination canvas.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
|
||||
if element.wrap {
|
||||
element.drawer.SetMaxWidth(bounds.Dx())
|
||||
element.drawer.SetMaxHeight(bounds.Dy())
|
||||
}
|
||||
|
||||
element.entity.DrawBackground(destination)
|
||||
|
||||
textBounds := element.drawer.LayoutBounds()
|
||||
foreground := element.theme.Color (
|
||||
tomo.ColorForeground,
|
||||
tomo.State { })
|
||||
element.drawer.Draw(destination, foreground, bounds.Min.Sub(textBounds.Min))
|
||||
}
|
||||
|
||||
// Copy copies the label's textto the clipboard.
|
||||
func (element *Label) Copy () {
|
||||
window := element.entity.Window()
|
||||
if window != nil {
|
||||
window.Copy(data.Bytes(data.MimePlain, []byte(element.text)))
|
||||
}
|
||||
}
|
||||
|
||||
// EmCollapse forces a minimum width and height upon the label. The width is
|
||||
// measured in emspaces, and the height is measured in lines. If a zero value is
|
||||
// given for a dimension, its minimum will be determined by the label's content.
|
||||
@ -122,22 +151,42 @@ func (element *Label) SetConfig (new tomo.Config) {
|
||||
element.entity.Invalidate()
|
||||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Label) Draw (destination canvas.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
|
||||
if element.wrap {
|
||||
element.drawer.SetMaxWidth(bounds.Dx())
|
||||
element.drawer.SetMaxHeight(bounds.Dy())
|
||||
func (element *Label) HandleMouseDown (
|
||||
position image.Point,
|
||||
button input.Button,
|
||||
modifiers input.Modifiers,
|
||||
) {
|
||||
if button == input.ButtonRight {
|
||||
element.contextMenu(position)
|
||||
}
|
||||
}
|
||||
|
||||
func (element *Label) HandleMouseUp (
|
||||
position image.Point,
|
||||
button input.Button,
|
||||
modifiers input.Modifiers,
|
||||
) { }
|
||||
|
||||
func (element *Label) contextMenu (position image.Point) {
|
||||
window := element.entity.Window()
|
||||
menu, err := window.NewMenu(image.Rectangle { position, position })
|
||||
if err != nil { return }
|
||||
|
||||
closeAnd := func (callback func ()) func () {
|
||||
return func () { callback(); menu.Close() }
|
||||
}
|
||||
|
||||
element.entity.DrawBackground(destination)
|
||||
copyButton := NewButton("Copy")
|
||||
copyButton.ShowText(false)
|
||||
copyButton.SetIcon(tomo.IconCopy)
|
||||
copyButton.OnClick(closeAnd(element.Copy))
|
||||
|
||||
textBounds := element.drawer.LayoutBounds()
|
||||
foreground := element.theme.Color (
|
||||
tomo.ColorForeground,
|
||||
tomo.State { })
|
||||
element.drawer.Draw(destination, foreground, bounds.Min.Sub(textBounds.Min))
|
||||
menu.Adopt (NewHBox (
|
||||
SpaceNone,
|
||||
copyButton,
|
||||
))
|
||||
copyButton.Focus()
|
||||
menu.Show()
|
||||
}
|
||||
|
||||
func (element *Label) updateMinimumSize () {
|
||||
|
@ -151,7 +151,8 @@ func (element *TextBox) HandleMouseDown (
|
||||
if !element.Enabled() { return }
|
||||
element.Focus()
|
||||
|
||||
if button == input.ButtonLeft {
|
||||
switch button {
|
||||
case input.ButtonLeft:
|
||||
runeIndex := element.atPosition(position)
|
||||
if runeIndex == -1 { return }
|
||||
|
||||
@ -165,6 +166,18 @@ func (element *TextBox) HandleMouseDown (
|
||||
}
|
||||
|
||||
element.entity.Invalidate()
|
||||
case input.ButtonRight:
|
||||
element.contextMenu(position)
|
||||
}
|
||||
}
|
||||
|
||||
func (element *TextBox) HandleMouseUp (
|
||||
position image.Point,
|
||||
button input.Button,
|
||||
modifiers input.Modifiers,
|
||||
) {
|
||||
if button == input.ButtonLeft {
|
||||
element.dragging = 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,23 +230,12 @@ func (element *TextBox) atPosition (position image.Point) int {
|
||||
fixedutil.Pt(position.Sub(offset).Add(textBoundsMin)))
|
||||
}
|
||||
|
||||
func (element *TextBox) HandleMouseUp (
|
||||
position image.Point,
|
||||
button input.Button,
|
||||
modifiers input.Modifiers,
|
||||
) {
|
||||
if button == input.ButtonLeft {
|
||||
element.dragging = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers) {
|
||||
if element.onKeyDown != nil && element.onKeyDown(key, modifiers) {
|
||||
return
|
||||
}
|
||||
|
||||
scrollMemory := element.scroll
|
||||
altered := true
|
||||
textChanged := false
|
||||
switch {
|
||||
case key == input.KeyEnter:
|
||||
@ -269,6 +271,8 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers)
|
||||
element.dot,
|
||||
modifiers.Control)
|
||||
}
|
||||
element.scrollToCursor()
|
||||
element.entity.Invalidate()
|
||||
|
||||
case key == input.KeyRight:
|
||||
if modifiers.Shift {
|
||||
@ -282,38 +286,24 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers)
|
||||
element.dot,
|
||||
modifiers.Control)
|
||||
}
|
||||
element.scrollToCursor()
|
||||
element.entity.Invalidate()
|
||||
|
||||
case key == 'a' && modifiers.Control:
|
||||
element.dot.Start = 0
|
||||
element.dot.End = len(element.text)
|
||||
element.scrollToCursor()
|
||||
element.entity.Invalidate()
|
||||
|
||||
case key == 'x' && modifiers.Control:
|
||||
var lifted []rune
|
||||
element.text, element.dot, lifted = textmanip.Lift (
|
||||
element.text,
|
||||
element.dot)
|
||||
if lifted != nil {
|
||||
element.clipboardPut(lifted)
|
||||
textChanged = true
|
||||
}
|
||||
case key == 'x' && modifiers.Control: element.Cut()
|
||||
case key == 'c' && modifiers.Control: element.Copy()
|
||||
case key == 'v' && modifiers.Control: element.Paste()
|
||||
|
||||
case key == 'c' && modifiers.Control:
|
||||
element.clipboardPut(element.dot.Slice(element.text))
|
||||
|
||||
case key == 'v' && modifiers.Control:
|
||||
window := element.entity.Window()
|
||||
if window == nil { break }
|
||||
window.Paste (func (d data.Data, err error) {
|
||||
if err != nil { return }
|
||||
reader, ok := d[data.MimePlain]
|
||||
if !ok { return }
|
||||
bytes, _ := io.ReadAll(reader)
|
||||
element.text, element.dot = textmanip.Type (
|
||||
element.text,
|
||||
element.dot,
|
||||
[]rune(string(bytes))...)
|
||||
element.notifyAsyncTextChange()
|
||||
})
|
||||
case key == input.KeyMenu:
|
||||
pos := fixedutil.RoundPt(element.valueDrawer.PositionAt(element.dot.End)).
|
||||
Add(element.textOffset())
|
||||
pos.Y += element.valueDrawer.LineHeight().Round()
|
||||
element.contextMenu(pos)
|
||||
|
||||
case key.Printable():
|
||||
element.text, element.dot = textmanip.Type (
|
||||
@ -321,29 +311,54 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers)
|
||||
element.dot,
|
||||
rune(key))
|
||||
textChanged = true
|
||||
|
||||
default:
|
||||
altered = false
|
||||
}
|
||||
|
||||
if textChanged {
|
||||
element.runOnChange()
|
||||
element.valueDrawer.SetText(element.text)
|
||||
}
|
||||
|
||||
if altered {
|
||||
element.scrollToCursor()
|
||||
element.entity.Invalidate()
|
||||
}
|
||||
|
||||
if (textChanged || scrollMemory != element.scroll) {
|
||||
element.entity.NotifyScrollBoundsChange()
|
||||
}
|
||||
|
||||
if altered {
|
||||
element.entity.Invalidate()
|
||||
}
|
||||
|
||||
// Cut cuts the selected text in the text box and places it in the clipboard.
|
||||
func (element *TextBox) Cut () {
|
||||
var lifted []rune
|
||||
element.text, element.dot, lifted = textmanip.Lift (
|
||||
element.text,
|
||||
element.dot)
|
||||
if lifted != nil {
|
||||
element.clipboardPut(lifted)
|
||||
element.notifyAsyncTextChange()
|
||||
}
|
||||
}
|
||||
|
||||
// Copy copies the selected text in the text box and places it in the clipboard.
|
||||
func (element *TextBox) Copy () {
|
||||
element.clipboardPut(element.dot.Slice(element.text))
|
||||
}
|
||||
|
||||
// Paste pastes text data from the clipboard into the text box.
|
||||
func (element *TextBox) Paste () {
|
||||
window := element.entity.Window()
|
||||
if window == nil { return }
|
||||
window.Paste (func (d data.Data, err error) {
|
||||
if err != nil { return }
|
||||
reader, ok := d[data.MimePlain]
|
||||
if !ok { return }
|
||||
bytes, _ := io.ReadAll(reader)
|
||||
element.text, element.dot = textmanip.Type (
|
||||
element.text,
|
||||
element.dot,
|
||||
[]rune(string(bytes))...)
|
||||
element.notifyAsyncTextChange()
|
||||
})
|
||||
}
|
||||
|
||||
func (element *TextBox) HandleKeyUp(key input.Key, modifiers input.Modifiers) { }
|
||||
|
||||
// SetPlaceholder sets the element's placeholder text.
|
||||
@ -482,6 +497,42 @@ func (element *TextBox) SetConfig (new tomo.Config) {
|
||||
element.entity.Invalidate()
|
||||
}
|
||||
|
||||
func (element *TextBox) contextMenu (position image.Point) {
|
||||
window := element.entity.Window()
|
||||
menu, err := window.NewMenu(image.Rectangle { position, position })
|
||||
if err != nil { return }
|
||||
|
||||
closeAnd := func (callback func ()) func () {
|
||||
return func () { callback(); menu.Close() }
|
||||
}
|
||||
|
||||
cutButton := NewButton("Cut")
|
||||
cutButton.ShowText(false)
|
||||
cutButton.SetIcon(tomo.IconCut)
|
||||
cutButton.SetEnabled(!element.dot.Empty())
|
||||
cutButton.OnClick(closeAnd(element.Cut))
|
||||
|
||||
copyButton := NewButton("Copy")
|
||||
copyButton.ShowText(false)
|
||||
copyButton.SetIcon(tomo.IconCopy)
|
||||
copyButton.SetEnabled(!element.dot.Empty())
|
||||
copyButton.OnClick(closeAnd(element.Copy))
|
||||
|
||||
pasteButton := NewButton("Paste")
|
||||
pasteButton.ShowText(false)
|
||||
pasteButton.SetIcon(tomo.IconPaste)
|
||||
pasteButton.OnClick(closeAnd(element.Paste))
|
||||
|
||||
menu.Adopt (NewHBox (
|
||||
SpaceNone,
|
||||
pasteButton,
|
||||
copyButton,
|
||||
cutButton,
|
||||
))
|
||||
pasteButton.Focus()
|
||||
menu.Show()
|
||||
}
|
||||
|
||||
func (element *TextBox) runOnChange () {
|
||||
if element.onChange != nil {
|
||||
element.onChange()
|
||||
|
Reference in New Issue
Block a user