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,
|
event xevent.ExposeEvent,
|
||||||
) {
|
) {
|
||||||
_, region := window.compressExpose(*event.ExposeEvent)
|
_, region := window.compressExpose(*event.ExposeEvent)
|
||||||
window.system.afterEvent()
|
|
||||||
window.pushRegion(region)
|
window.pushRegion(region)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,8 +78,6 @@ func (window *window) handleConfigureNotify (
|
|||||||
window.child.Invalidate()
|
window.child.Invalidate()
|
||||||
window.child.InvalidateLayout()
|
window.child.InvalidateLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
window.system.afterEvent()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,8 +137,6 @@ func (window *window) handleKeyPress (
|
|||||||
focused, ok := window.focused.element.(tomo.KeyboardTarget)
|
focused, ok := window.focused.element.(tomo.KeyboardTarget)
|
||||||
if ok { focused.HandleKeyDown(key, modifiers) }
|
if ok { focused.HandleKeyDown(key, modifiers) }
|
||||||
}
|
}
|
||||||
|
|
||||||
window.system.afterEvent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *window) handleKeyRelease (
|
func (window *window) handleKeyRelease (
|
||||||
@ -174,8 +169,6 @@ func (window *window) handleKeyRelease (
|
|||||||
if window.focused != nil {
|
if window.focused != nil {
|
||||||
focused, ok := window.focused.element.(tomo.KeyboardTarget)
|
focused, ok := window.focused.element.(tomo.KeyboardTarget)
|
||||||
if ok { focused.HandleKeyUp(key, modifiers) }
|
if ok { focused.HandleKeyUp(key, modifiers) }
|
||||||
|
|
||||||
window.system.afterEvent()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,8 +213,6 @@ func (window *window) handleButtonPress (
|
|||||||
}
|
}
|
||||||
underneath.forMouseTargetContainers(callback)
|
underneath.forMouseTargetContainers(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.system.afterEvent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *window) handleButtonRelease (
|
func (window *window) handleButtonRelease (
|
||||||
@ -252,8 +243,6 @@ func (window *window) handleButtonRelease (
|
|||||||
}
|
}
|
||||||
dragging.forMouseTargetContainers(callback)
|
dragging.forMouseTargetContainers(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.system.afterEvent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *window) handleMotionNotify (
|
func (window *window) handleMotionNotify (
|
||||||
@ -279,8 +268,6 @@ func (window *window) handleMotionNotify (
|
|||||||
child.HandleMotion(image.Pt(x, y))
|
child.HandleMotion(image.Pt(x, y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.system.afterEvent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *window) handleSelectionNotify (
|
func (window *window) handleSelectionNotify (
|
||||||
|
@ -67,12 +67,12 @@ func (backend *Backend) Run () (err error) {
|
|||||||
<- pingAfter
|
<- pingAfter
|
||||||
case callback := <- backend.doChannel:
|
case callback := <- backend.doChannel:
|
||||||
callback()
|
callback()
|
||||||
for _, window := range backend.windows {
|
|
||||||
window.system.afterEvent()
|
|
||||||
}
|
|
||||||
case <- pingQuit:
|
case <- pingQuit:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
for _, window := range backend.windows {
|
||||||
|
window.system.afterEvent()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package elements
|
package elements
|
||||||
|
|
||||||
|
import "image"
|
||||||
import "golang.org/x/image/math/fixed"
|
import "golang.org/x/image/math/fixed"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo"
|
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/canvas"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/textdraw"
|
import "git.tebibyte.media/sashakoshka/tomo/textdraw"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/default/theme"
|
import "git.tebibyte.media/sashakoshka/tomo/default/theme"
|
||||||
@ -48,6 +51,32 @@ func (element *Label) Entity () tomo.Entity {
|
|||||||
return element.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
|
// 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
|
// 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.
|
// 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()
|
element.entity.Invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw causes the element to draw to the specified destination canvas.
|
func (element *Label) HandleMouseDown (
|
||||||
func (element *Label) Draw (destination canvas.Canvas) {
|
position image.Point,
|
||||||
bounds := element.entity.Bounds()
|
button input.Button,
|
||||||
|
modifiers input.Modifiers,
|
||||||
|
) {
|
||||||
|
if button == input.ButtonRight {
|
||||||
|
element.contextMenu(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if element.wrap {
|
func (element *Label) HandleMouseUp (
|
||||||
element.drawer.SetMaxWidth(bounds.Dx())
|
position image.Point,
|
||||||
element.drawer.SetMaxHeight(bounds.Dy())
|
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()
|
menu.Adopt (NewHBox (
|
||||||
foreground := element.theme.Color (
|
SpaceNone,
|
||||||
tomo.ColorForeground,
|
copyButton,
|
||||||
tomo.State { })
|
))
|
||||||
element.drawer.Draw(destination, foreground, bounds.Min.Sub(textBounds.Min))
|
copyButton.Focus()
|
||||||
|
menu.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *Label) updateMinimumSize () {
|
func (element *Label) updateMinimumSize () {
|
||||||
|
@ -151,7 +151,8 @@ func (element *TextBox) HandleMouseDown (
|
|||||||
if !element.Enabled() { return }
|
if !element.Enabled() { return }
|
||||||
element.Focus()
|
element.Focus()
|
||||||
|
|
||||||
if button == input.ButtonLeft {
|
switch button {
|
||||||
|
case input.ButtonLeft:
|
||||||
runeIndex := element.atPosition(position)
|
runeIndex := element.atPosition(position)
|
||||||
if runeIndex == -1 { return }
|
if runeIndex == -1 { return }
|
||||||
|
|
||||||
@ -165,6 +166,18 @@ func (element *TextBox) HandleMouseDown (
|
|||||||
}
|
}
|
||||||
|
|
||||||
element.entity.Invalidate()
|
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)))
|
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) {
|
func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers) {
|
||||||
if element.onKeyDown != nil && element.onKeyDown(key, modifiers) {
|
if element.onKeyDown != nil && element.onKeyDown(key, modifiers) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollMemory := element.scroll
|
scrollMemory := element.scroll
|
||||||
altered := true
|
|
||||||
textChanged := false
|
textChanged := false
|
||||||
switch {
|
switch {
|
||||||
case key == input.KeyEnter:
|
case key == input.KeyEnter:
|
||||||
@ -269,6 +271,8 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers)
|
|||||||
element.dot,
|
element.dot,
|
||||||
modifiers.Control)
|
modifiers.Control)
|
||||||
}
|
}
|
||||||
|
element.scrollToCursor()
|
||||||
|
element.entity.Invalidate()
|
||||||
|
|
||||||
case key == input.KeyRight:
|
case key == input.KeyRight:
|
||||||
if modifiers.Shift {
|
if modifiers.Shift {
|
||||||
@ -282,27 +286,66 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers)
|
|||||||
element.dot,
|
element.dot,
|
||||||
modifiers.Control)
|
modifiers.Control)
|
||||||
}
|
}
|
||||||
|
element.scrollToCursor()
|
||||||
|
element.entity.Invalidate()
|
||||||
|
|
||||||
case key == 'a' && modifiers.Control:
|
case key == 'a' && modifiers.Control:
|
||||||
element.dot.Start = 0
|
element.dot.Start = 0
|
||||||
element.dot.End = len(element.text)
|
element.dot.End = len(element.text)
|
||||||
|
element.scrollToCursor()
|
||||||
|
element.entity.Invalidate()
|
||||||
|
|
||||||
case key == 'x' && modifiers.Control:
|
case key == 'x' && modifiers.Control: element.Cut()
|
||||||
|
case key == 'c' && modifiers.Control: element.Copy()
|
||||||
|
case key == 'v' && modifiers.Control: element.Paste()
|
||||||
|
|
||||||
|
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 (
|
||||||
|
element.text,
|
||||||
|
element.dot,
|
||||||
|
rune(key))
|
||||||
|
textChanged = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if textChanged {
|
||||||
|
element.runOnChange()
|
||||||
|
element.valueDrawer.SetText(element.text)
|
||||||
|
element.scrollToCursor()
|
||||||
|
element.entity.Invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textChanged || scrollMemory != element.scroll) {
|
||||||
|
element.entity.NotifyScrollBoundsChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut cuts the selected text in the text box and places it in the clipboard.
|
||||||
|
func (element *TextBox) Cut () {
|
||||||
var lifted []rune
|
var lifted []rune
|
||||||
element.text, element.dot, lifted = textmanip.Lift (
|
element.text, element.dot, lifted = textmanip.Lift (
|
||||||
element.text,
|
element.text,
|
||||||
element.dot)
|
element.dot)
|
||||||
if lifted != nil {
|
if lifted != nil {
|
||||||
element.clipboardPut(lifted)
|
element.clipboardPut(lifted)
|
||||||
textChanged = true
|
element.notifyAsyncTextChange()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case key == 'c' && modifiers.Control:
|
// 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))
|
element.clipboardPut(element.dot.Slice(element.text))
|
||||||
|
}
|
||||||
|
|
||||||
case key == 'v' && modifiers.Control:
|
// Paste pastes text data from the clipboard into the text box.
|
||||||
|
func (element *TextBox) Paste () {
|
||||||
window := element.entity.Window()
|
window := element.entity.Window()
|
||||||
if window == nil { break }
|
if window == nil { return }
|
||||||
window.Paste (func (d data.Data, err error) {
|
window.Paste (func (d data.Data, err error) {
|
||||||
if err != nil { return }
|
if err != nil { return }
|
||||||
reader, ok := d[data.MimePlain]
|
reader, ok := d[data.MimePlain]
|
||||||
@ -314,34 +357,6 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers)
|
|||||||
[]rune(string(bytes))...)
|
[]rune(string(bytes))...)
|
||||||
element.notifyAsyncTextChange()
|
element.notifyAsyncTextChange()
|
||||||
})
|
})
|
||||||
|
|
||||||
case key.Printable():
|
|
||||||
element.text, element.dot = textmanip.Type (
|
|
||||||
element.text,
|
|
||||||
element.dot,
|
|
||||||
rune(key))
|
|
||||||
textChanged = true
|
|
||||||
|
|
||||||
default:
|
|
||||||
altered = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if textChanged {
|
|
||||||
element.runOnChange()
|
|
||||||
element.valueDrawer.SetText(element.text)
|
|
||||||
}
|
|
||||||
|
|
||||||
if altered {
|
|
||||||
element.scrollToCursor()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (textChanged || scrollMemory != element.scroll) {
|
|
||||||
element.entity.NotifyScrollBoundsChange()
|
|
||||||
}
|
|
||||||
|
|
||||||
if altered {
|
|
||||||
element.entity.Invalidate()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (element *TextBox) HandleKeyUp(key input.Key, modifiers input.Modifiers) { }
|
func (element *TextBox) HandleKeyUp(key input.Key, modifiers input.Modifiers) { }
|
||||||
@ -482,6 +497,42 @@ func (element *TextBox) SetConfig (new tomo.Config) {
|
|||||||
element.entity.Invalidate()
|
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 () {
|
func (element *TextBox) runOnChange () {
|
||||||
if element.onChange != nil {
|
if element.onChange != nil {
|
||||||
element.onChange()
|
element.onChange()
|
||||||
|
Reference in New Issue
Block a user