gui: change tracking for insert, edit, delete actions

This commit is contained in:
mappu 2024-07-06 11:04:19 +12:00
parent 0d3b90b879
commit 8af27f8834
3 changed files with 117 additions and 15 deletions

121
main.go
View File

@ -17,6 +17,11 @@ import (
const (
APPNAME = "yvbolt"
HOMEPAGE_URL = "https://code.ivysaur.me/yvbolt"
CO_INSERT = colors.ClYellow
CO_EDIT_IMPLICIT = colors.ClLightgreen
CO_EDIT_EXPLICIT = colors.ClGreen
CO_DELETE = colors.ClRed
)
type TMainForm struct {
@ -32,7 +37,10 @@ type TMainForm struct {
Tabs *vcl.TPageControl
propertiesBox *vcl.TMemo
contentBox *vcl.TStringGrid
insertRows []int32 // Rows in the StringGrid that are to-be-inserted
isEditing bool
insertRows map[int32]struct{} // Rows in the StringGrid that are to-be-inserted
deleteRows map[int32]struct{}
updateRows map[int32][]int32 // Row->cells that are to-be-updated
queryInput *vcl.TMemo
queryResult *vcl.TStringGrid
@ -233,14 +241,26 @@ func (f *TMainForm) OnFormCreate(sender vcl.IObject) {
dataInsertBtn.SetCaption("Insert")
dataInsertBtn.SetOnClick(f.OnDataInsertClick)
dataDelRowBtn := vcl.NewToolButton(dataButtonBar)
dataDelRowBtn.SetParent(dataButtonBar)
dataDelRowBtn.SetCaption("Delete Row")
dataDelRowBtn.SetOnClick(f.OnDataDeleteRowClick)
dataCommitBtn := vcl.NewToolButton(dataButtonBar)
dataCommitBtn.SetParent(dataButtonBar)
dataCommitBtn.SetCaption("Commit")
dataCommitBtn.SetOnClick(f.OnDataCommitClick)
f.contentBox = vcl.NewStringGrid(dataTab)
f.contentBox.SetParent(dataTab)
f.contentBox.BorderSpacing().SetLeft(MY_SPACING)
f.contentBox.BorderSpacing().SetRight(MY_SPACING)
f.contentBox.BorderSpacing().SetBottom(MY_SPACING)
f.contentBox.SetAlign(types.AlClient) // fill remaining space
f.contentBox.SetOptions(f.contentBox.Options().Include(types.GoThumbTracking, types.GoColSizing, types.GoDblClickAutoSize))
f.contentBox.SetOptions(f.contentBox.Options().Include(types.GoThumbTracking, types.GoColSizing, types.GoDblClickAutoSize, types.GoEditing))
f.contentBox.SetOnPrepareCanvas(f.OnDataPrepareCanvas)
f.contentBox.SetOnEditingDone(f.OnDataCellEdited)
f.contentBox.SetOnGetEditText(f.OnDataCellEditStarting)
f.contentBox.SetDefaultColWidth(MY_WIDTH)
vcl_stringgrid_clear(f.contentBox)
@ -510,7 +530,8 @@ func (f *TMainForm) RefreshCurrentItem() {
ndata := (*navData)(item.Data())
f.OnNavContextRefresh(item, ndata)
f.OnNavContextRefresh(item, ndata) // Refresh LHS pane/children
f.OnNavChange(f.Buckets, item) // Refresh RHS pane/data content
}
func (f *TMainForm) OnNavContextRefresh(item *vcl.TTreeNode, ndata *navData) {
@ -530,22 +551,66 @@ func (f *TMainForm) OnNavContextRefresh(item *vcl.TTreeNode, ndata *navData) {
}
func (f *TMainForm) OnDataPrepareCanvas(sender vcl.IObject, aCol, aRow int32, aState types.TGridDrawState) {
if len(f.insertRows) == 0 {
return // fast-path
if _, ok := f.deleteRows[aRow]; ok {
f.contentBox.Canvas().Brush().SetColor(CO_DELETE)
return
}
isInsertRow := false
for _, rr := range f.insertRows {
if rr == aRow {
isInsertRow = true
break
if _, ok := f.insertRows[aRow]; ok {
f.contentBox.Canvas().Brush().SetColor(CO_INSERT)
return
}
if er, ok := f.updateRows[aRow]; ok {
// This row is being edited
if int32slice_contains(er, aCol) {
f.contentBox.Canvas().Brush().SetColor(CO_EDIT_EXPLICIT)
} else {
f.contentBox.Canvas().Brush().SetColor(CO_EDIT_IMPLICIT)
}
return
}
}
// func (f *TMainForm) OnDataCellEditStarting(sender vcl.IObject, aCol, aRow int32, editor **vcl.TWinControl) {
func (f *TMainForm) OnDataCellEditStarting(sender vcl.IObject, aCol, aRow int32, value *string) {
f.isEditing = true
}
func (f *TMainForm) OnDataCellEdited(sender vcl.IObject) {
// The OnEditingDone event fires whenever the TStringGrid loses focus, even
// if editing was not currently taking place
// To detect real edits, set a flag in the OnSelectEditor event
if !f.isEditing {
return
}
f.isEditing = false
aRow := f.contentBox.Row()
aCol := f.contentBox.Col()
// If this is an insert row, no need to patch updateRows
if _, ok := f.insertRows[aRow]; ok {
return // nothing to do
}
if isInsertRow {
/*sender.(*vcl.TStringGrid)*/
f.contentBox.Canvas().Brush().SetColor(colors.ClYellow)
if chk, ok := f.updateRows[aRow]; ok {
if int32slice_contains(chk, aCol) {
// nothing to do
} else {
chk = append(chk, aCol)
f.updateRows[aRow] = chk
}
} else {
f.updateRows[aRow] = []int32{aCol}
}
// If this row was marked for deletion, this new event takes priority
delete(f.deleteRows, aRow)
// Signal repaint
f.contentBox.InvalidateRow(aRow)
}
func (f *TMainForm) OnDataInsertClick(sender vcl.IObject) {
@ -556,12 +621,35 @@ func (f *TMainForm) OnDataInsertClick(sender vcl.IObject) {
rpos := f.contentBox.RowCount()
f.contentBox.SetRowCount(rpos + 1)
f.insertRows = append(f.insertRows, rpos)
f.insertRows[rpos] = struct{}{}
// Scroll to bottom
f.contentBox.SetTopRow(rpos)
}
func (f *TMainForm) OnDataDeleteRowClick(sender vcl.IObject) {
if !f.contentBox.Enabled() {
return // Not an active data view
}
rpos := f.contentBox.Row()
f.deleteRows[rpos] = struct{}{}
// If this row was marked for edit, this takes priority
delete(f.updateRows, rpos)
// Repaint
f.contentBox.InvalidateRow(rpos)
}
func (f *TMainForm) OnDataCommitClick(sender vcl.IObject) {
if !f.contentBox.Enabled() {
return // Not an active data view
}
// TODO
}
func (f *TMainForm) OnNavContextClose(sender vcl.IObject) {
curItem := f.Buckets.Selected()
if curItem == nil {
@ -613,7 +701,10 @@ func (f *TMainForm) OnNavChange(sender vcl.IObject, node *vcl.TTreeNode) {
}
// Reset some controls that the render function is expected to populate
f.insertRows = nil
f.insertRows = make(map[int32]struct{})
f.updateRows = make(map[int32][]int32)
f.deleteRows = make(map[int32]struct{})
f.isEditing = false
f.propertiesBox.Clear()
vcl_stringgrid_clear(f.contentBox)

View File

@ -8,4 +8,14 @@ func box_interface[T any](input []T) []interface{} {
ret = append(ret, v)
}
return ret
}
func int32slice_contains(haystack []int32, needle int32) bool {
for _, v := range haystack {
if v == needle {
return true
}
}
return false
}

View File

@ -70,6 +70,7 @@ func vcl_stringgrid_clear(d *vcl.TStringGrid) {
d.SetRowCount(1) // The 0th row is the column headers
d.SetEnabled(false)
d.Columns().Clear()
d.Invalidate()
}
func vcl_stringgrid_columnwidths(d *vcl.TStringGrid) {