Coder Social home page Coder Social logo

Example of "Add new row"? about clui HOT 12 CLOSED

MostHated avatar MostHated commented on August 25, 2024
Example of "Add new row"?

from clui.

Comments (12)

VladimirMarkelov avatar VladimirMarkelov commented on August 25, 2024 2

So when I create a new row, I create it in dbCache and tmpAssetData, but nothing triggers for it to redraw with the newly added row

I have not tested it, but I'd try the following flow on successful row addition: 1) update dbCache and add a new data row to it 2) set new row count with SetRowCount 3) call screen refresh with ui.PutEvent(ui.Event{Type: ui.EventRedraw})

from clui.

VladimirMarkelov avatar VladimirMarkelov commented on August 25, 2024

Neither worked and just gave an out of range error

What failed? I have no clue what the error was and what line if crashed. Only gueses:

absIndex := firstRow + i
...
	d.data[i] = make([]string, columnInTable, columnInTable)
	d.data[i][0] = tmpAssetData[absIndex].AssetCode`
  1. Do you have enough items in tmpAssetData, so len(tmpAssetData) is firstRow+rowCount-1?
  2. Have you pre-filled tmpAssetData?

If you set TableView row and col count to the same values(rowCount, colCount), you do not need to write anything for ActionNew

from clui.

MostHated avatar MostHated commented on August 25, 2024

No, sorry, there is nothing in there at the time. I removed the data I created by hand in code. I am trying to find out how to create a new row if there are 0 rows. Pretend it's a brand new application with 0 data or rows. I am trying to figure out how to use "add new row" action to start fresh and add data to an empty dataset.

from clui.

VladimirMarkelov avatar VladimirMarkelov commented on August 25, 2024

I see. I'll try this case myself

from clui.

MostHated avatar MostHated commented on August 25, 2024

Here is what I have been attempting to come up with:

I feel like I might be close, but I am just not quite sure.

		case ui.TableActionNew:
			//action = "Add new row"
			c := ev.Col
			r := ev.Row
			var editVal = TableEdit{Row: r, Col: c}

			dlg := CreateEditableDialog(fmt.Sprintf("%s:", TxtNewAssetCodeValue), "") // --- A custom popup I made so I can make more adjustments than the default one had
			dlg.View.SetSize(35, 10)
			dlg.OnClose(func() {
				switch dlg.Result() {
				case ui.DialogButton1:
					editVal.NewVal = dlg.EditResult()
					if tmpAssetData == nil {
						var newInfo = ui.ColumnDrawInfo{Row: 0, Col: 0}
						cache.data = make([][]string, rowCount, rowCount) // ------ I have this because in preload I have at the beginning  if tmpAssetData == nil { return }
						(func(info *ui.ColumnDrawInfo) {
							cache.data[newInfo.Row][newInfo.Col] = editVal.NewVal  // ------------------------------195
							info.Text = cache.data[newInfo.Row][newInfo.Col]
						})(&newInfo)

						//cache.NewValue(0, 0, editVal.NewVal)
					} else {
						cache.NewValue(len(tmpAssetData), 0, editVal.NewVal)
					}

					ui.PutEvent(ui.Event{Type: ui.EventRedraw})
				}
			})
panic: runtime error: index out of range

goroutine 1 [running]:
github.com/instance-id/GoUI/elements.CreateTableDialog.func3.3.1(...)
        /home/mosthated/_dev/programming/go/src/github.com/instance-id/GoUI/elements/CreateTableDialog.go:195
github.com/instance-id/GoUI/elements.CreateTableDialog.func3.3()
        /home/mosthated/_dev/programming/go/src/github.com/instance-id/GoUI/elements/CreateTableDialog.go:197 +0x22a

----------- This was my whole table page, just to give it some context.

package elements

import (
	"fmt"
	. "github.com/instance-id/GoUI/text"
	. "github.com/instance-id/GoUI/utils"
	ui "github.com/instance-id/clui"
	term "github.com/nsf/termbox-go"
)

var tmpAssetData []*AssetDetails

type dbCache struct {
	firstRow int        // previous first visible row
	rowCount int        // previous visible row count
	data     [][]string // cache - contains at least 'rowCount' rows from DB
}

const columnInTable = 7

func (d *dbCache) preload(firstRow, rowCount int) {

	if tmpAssetData == nil {
		return
	}

	if firstRow == d.firstRow && rowCount == d.rowCount {
		// fast path: view area is the same, return immediately
		return
	}

	d.data = make([][]string, rowCount, rowCount)
	for i := 0; i < rowCount; i++ {
		absIndex := firstRow + i
		d.data[i] = make([]string, columnInTable, columnInTable)
		d.data[i][0] = tmpAssetData[absIndex].AssetCode
		d.data[i][1] = tmpAssetData[absIndex].AssetName
		d.data[i][2] = tmpAssetData[absIndex].AssetApiKey
		d.data[i][3] = tmpAssetData[absIndex].AssetRole
		d.data[i][4] = tmpAssetData[absIndex].AssetVersion
		d.data[i][5] = tmpAssetData[absIndex].AssetReplaced
		d.data[i][6] = tmpAssetData[absIndex].ReplaceDate
	}

	// do not forget to save the last values
	d.firstRow = firstRow
	d.rowCount = rowCount
}


// --- Custom function for editing place ---------------------------------
func (d *dbCache) NewValue(row, col int, newText string) {
	d.data[row][col] = newText
}

func (d *dbCache) value(row, col int) string {
	rowId := row - d.firstRow
	if rowId >= len(d.data) {
		return ""
	}
	rowValues := d.data[rowId]
	if col >= len(rowValues) {
		return ""
	}
	return rowValues[col]
}


// --- Window type for data table ----------------------------------------
func CreateTableDialog(btn *ui.ButtonNoShadow, tableTitle string) {
	tableDialog := new(TableDialog)

	// --- Obtain terminal overall size ----------------------------------
	cw, ch := term.Size()

	// --- Save current values to temp value until saved -----------------
	tmpAssetData = Asset

	// --- Create new popup window for table data ------------------------
	tableDialog.View = ui.AddWindow(cw/2-75, ch/2-16, ui.AutoSize, ui.AutoSize, TxtAssetDetails)
	ui.WindowManager().BeginUpdate()
	defer ui.WindowManager().EndUpdate()
	tableDialog.View.SetGaps(1, ui.KeepValue)
	tableDialog.View.SetModal(true)
	tableDialog.View.SetPack(ui.Vertical)

	tableDialog.Frame = NewFramedWindowInput(tableDialog.View, "", nil)

	// --- Create data table ---------------------------------------------
	td := ui.CreateTableView(tableDialog.Frame, 145, 15, 1)
	ui.ActivateControl(tableDialog.Frame, td)

	cache := &dbCache{firstRow: -1}
	rowCount := len(Asset)
	td.SetShowLines(true)
	td.SetShowRowNumber(true)
	td.SetRowCount(rowCount)

	cols := []ui.Column{
		ui.Column{Title: "Asset Code", Width: 5, Alignment: ui.AlignLeft},
		ui.Column{Title: "Asset Name", Width: 50, Alignment: ui.AlignLeft},
		ui.Column{Title: "Asset APIKey", Width: 30, Alignment: ui.AlignLeft},
		ui.Column{Title: "Asset RoleId", Width: 20, Alignment: ui.AlignLeft},
		ui.Column{Title: "Version", Width: 7, Alignment: ui.AlignLeft},
		ui.Column{Title: "Replaced?", Width: 10, Alignment: ui.AlignLeft},
		ui.Column{Title: "Replace Date", Width: 12, Alignment: ui.AlignLeft},
	}
	td.SetColumns(cols)

	td.OnBeforeDraw(func(col, row, colCnt, rowCnt int) {
		cache.preload(row, rowCnt)
		l, t, w, h := td.VisibleArea()
		tableDialog.Frame.SetTitle(fmt.Sprintf("Caching: %d:%d - %dx%d", l, t, w, h))
	})
	td.OnDrawCell(func(info *ui.ColumnDrawInfo) {
		info.Text = cache.value(info.Row, info.Col)
	})

	td.OnAction(func(ev ui.TableEvent) {
		// btns := []string{TxtApplyBtn, TxtCancelBtn}
		//var action string
		switch ev.Action {
		case ui.TableActionSort:
			//action = "Sort table"
		case ui.TableActionEdit:
			c := ev.Col
			r := ev.Row
			var newInfo = ui.ColumnDrawInfo{Row: r, Col: c}
			var editVal = TableEdit{Row: r, Col: c}

			(func(info *ui.ColumnDrawInfo) {
				editVal.OldVal = cache.value(info.Row, info.Col)
			})(&newInfo)

			dlg := CreateEditableDialog(fmt.Sprintf("%s: %s", TxtEditing, editVal.OldVal), editVal.OldVal)
			dlg.View.SetSize(35, 10)
			dlg.View.BaseControl.SetSize(35, 10)
			dlg.OnClose(func() {
				switch dlg.Result() {
				case ui.DialogButton1:
					editVal.NewVal = dlg.EditResult()
					cache.NewValue(editVal.Row, editVal.Col, editVal.NewVal)
					ui.PutEvent(ui.Event{Type: ui.EventRedraw})
				}
			})
			return
		case ui.TableActionNew:
			//action = "Add new row"
			c := ev.Col
			r := ev.Row
			var editVal = TableEdit{Row: r, Col: c}

			dlg := CreateEditableDialog(fmt.Sprintf("%s:", TxtNewAssetCodeValue), "")
			dlg.View.SetSize(35, 10)
			dlg.View.BaseControl.SetSize(35, 10)
			dlg.OnClose(func() {
				switch dlg.Result() {
				case ui.DialogButton1:
					editVal.NewVal = dlg.EditResult()
					if tmpAssetData == nil {
						var newInfo = ui.ColumnDrawInfo{Row: 0, Col: 0}
						cache.data = make([][]string, rowCount, rowCount)
						(func(info *ui.ColumnDrawInfo) {
							cache.data[newInfo.Row][newInfo.Col] = editVal.NewVal
							info.Text = cache.data[newInfo.Row][newInfo.Col]
						})(&newInfo)
					} else {
						cache.NewValue(len(tmpAssetData), 0, editVal.NewVal)
					}

					ui.PutEvent(ui.Event{Type: ui.EventRedraw})
				}
			})
		case ui.TableActionDelete:
			//action = "Delete row"
		default:
			//action = "Unknown action"
		}

		//dlg := ui.CreateConfirmationDialog(
		//	"<c:blue>"+action,
		//	"Click any button or press <c:yellow>SPACE<c:> to close the dialog",
		//	btns, ui.DialogButton1)
		//dlg.OnClose(func() {})
	})

	btnFrame := ui.CreateFrame(tableDialog.Frame, 1, 1, ui.BorderNone, ui.Fixed)
	btnFrame.SetPaddings(1, 1)
	textFrame := ui.CreateFrame(btnFrame, 1, 1, ui.BorderNone, ui.Fixed)
	textFrame.SetPack(ui.Vertical)

	ui.CreateLabel(textFrame, ui.AutoSize, ui.AutoSize, TxtInstructs1, ui.Fixed)
	ui.CreateLabel(textFrame, ui.AutoSize, ui.AutoSize, TxtInstructs2, ui.Fixed)
	ui.CreateLabel(textFrame, ui.AutoSize, ui.AutoSize, TxtInstructs3, ui.Fixed)

	// --- Window Controls -----------------------------------------------
	ui.CreateFrame(btnFrame, 1, 1, ui.BorderNone, 1)
	BtnSave := ui.CreateButton(btnFrame, 15, 1, TxtSaveBtn, ui.Fixed)
	BtnSave.OnClick(func(ev ui.Event) {
		Asset = tmpAssetData
		btn.SetEnabled(true)
	})
	BtnClose := ui.CreateButton(btnFrame, 15, 1, TxtCloseBtn, ui.Fixed)
	BtnClose.OnClick(func(ev ui.Event) {
		ui.WindowManager().DestroyWindow(tableDialog.View)
		btn.SetEnabled(true)
	})

	BtnSave.SetActive(false)
	BtnClose.SetActive(false)
}

from clui.

VladimirMarkelov avatar VladimirMarkelov commented on August 25, 2024

Thanks for the sources, but it is still unclear:

  1. It displays error at L195-197. If I copy-paste the source, in my editor it is:
195
196	// --- Window Controls -----------------------------------------------
197	ui.CreateFrame(btnFrame, 1, 1, ui.BorderNone, 1)

That looks incorrect.

  1. How big is Asset? I see rowCount := len(Asset), and if Asset has fewer items than 7, it may fail inside preload at any line that is like d.data[i][N] = tmpAssetData[absIndex].AssetCode (L36-L42)

from clui.

VladimirMarkelov avatar VladimirMarkelov commented on August 25, 2024

Oh, sorry. I just opened your message in email and saw that you had marked the line that fails :)

But the question #2 is still unclear: What the size of Asset? since rowcCount=len(Asset). if it is empty than it is clear why L195 panics.

Another point. Quotation from your code:

cache.data = make([][]string, rowCount, rowCount)
 (func(info *ui.ColumnDrawInfo) {
   cache.data[newInfo.Row][newInfo.Col] = editVal.NewVal  // ------------------------------195

you allocated memory for parent slice. But where is allocation for the nested one? I mean something like this:

cache.data = make([][]string, rowCount, rowCount)
cache.data[0] = make([]string, colCount, colCount)

from clui.

MostHated avatar MostHated commented on August 25, 2024

It is no worries. I think I almost got it. There were a few things I had to change. I think I should get it worked out shortly though!

from clui.

MostHated avatar MostHated commented on August 25, 2024

I am super close. I have adding a new row working if at least some data exists. I just have to make it so it can add one if none exists. At least I am on the right track. : D

It is definitely not the most clean, I will have to go back through and clean it up. I noticed I forgot to ever actually update the tmpAssetData when I was updating the table. I was only updating d.data, so I had to create a way to update that, as well as the tables data. (It will probably never be more than 7 rows, most of the time it will be 1-2 at most anyways). I had to add another struct as a container for AssetData and create the test data first a new way.

type AssetContainer struct {
	AD []AssetDetails
}

	Asset = &AssetContainer{AD: []AssetDetails{
		{AssetCode: "UFPS1",
			AssetName:     "UFPS : Ultimate FPS",
			AssetApiKey:   "123123112312312323123123123",
			AssetRole:     "123123123123123123",
			AssetReplaced: "Yes",
			AssetVersion:  "1",
			ReplaceDate:   "2018-06-06"},
		{
			AssetCode:     "UFPS2",
			AssetName:     "UFP2 : Ultimate FPS",
			AssetApiKey:   "123123112312312323123123123",
			AssetRole:     "123123123123123123",
			AssetReplaced: "Yes",
			AssetVersion:  "1",
			ReplaceDate:   "2018-06-06"}},
	}

Once I did that, when I updated the data from within the table I had to update both the actual data and the tables display of the data.

// --- This is in the TableActionEdit ----------------------------------------
dlg.OnClose(func() {
				switch dlg.Result() {
				case ui.DialogButton1:
					editVal.NewVal = dlg.EditResult()
					cache.UpdateData(editVal.Row, editVal.Col, editVal.NewVal)
					ui.PutEvent(ui.Event{Type: ui.EventRedraw})
				}
			})

// --- This updates both the table and tmpAssetData ---------------------------
// -- There may be a cleaner / smaller way of doing this ----------------------
func (d *dbCache) UpdateData(row int, col int, data string) []AssetDetails {

	switch col {
	case 0:
		tmpAssetData.AD[row].AssetCode = data
		d.data[row][col] = tmpAssetData.AD[row].AssetCode
	case 1:
		tmpAssetData.AD[row].AssetName = data
		d.data[row][col] = tmpAssetData.AD[row].AssetName
	case 2:
		tmpAssetData.AD[row].AssetApiKey = data
		d.data[row][col] = tmpAssetData.AD[row].AssetApiKey
	case 3:
		tmpAssetData.AD[row].AssetRole = data
		d.data[row][col] = tmpAssetData.AD[row].AssetRole
	case 4:
		tmpAssetData.AD[row].AssetVersion = data
		d.data[row][col] = tmpAssetData.AD[row].AssetVersion
	case 5:
		tmpAssetData.AD[row].AssetReplaced = data
		d.data[row][col] = tmpAssetData.AD[row].AssetReplaced
	case 6:
		tmpAssetData.AD[row].ReplaceDate = data
		d.data[row][col] = tmpAssetData.AD[row].ReplaceDate
	}

	return tmpAssetData.AD
}

Creating the container for the actual AssetData then allowed me to do this:

func (a *AssetContainer) AddNewAsset(asset AssetDetails) []AssetDetails {
	a.AD = append(a.AD, asset)
	return a.AD
}

So now in TableACtionNew, I have this:

			dlg.OnClose(func() {
				switch dlg.Result() {
				case ui.DialogButton1:
					editVal.NewVal = dlg.EditResult()
					if tmpAssetData.AD == nil {
						var newInfo = ui.ColumnDrawInfo{Row: 0, Col: 0}
						cache.data = make([][]string, rowCount, rowCount)
						data := cache.UpdateData(newInfo.Row, newInfo.Col, editVal.NewVal)
						tmpAssetData.AD = data
					} else {
						details := AssetDetails{AssetCode: editVal.NewVal}
						tmpAssetData.AddNewAsset(details)
						cache.AddNewRow(editVal.NewVal)
					}
				}

Seems to work pretty well in adding a new row. That 3rd one in the pic is the new one. I used this code to add it to the table:

func (d *dbCache) AddNewRow(newAsset string) {
	data := []string{newAsset, "", "", "", "", "", ""}
	d.data = append(d.data, data)
}

from clui.

MostHated avatar MostHated commented on August 25, 2024

EDIT -- Nevermind about my question below, I created a work around that ends up doing pretty much the same thing by doing the following:

	var newInfo = ui.ColumnDrawInfo{Row: 0, Col: 0}
	var restrictor = 0

	td.OnActive(func(active bool) {
		if (func(info *ui.ColumnDrawInfo) string {
			return cache.value(info.Row, info.Col)
		})(&newInfo) == "" {
			if restrictor == 0 {
				cache.CreateNewData(tmpAssetData)
				restrictor = 1
			}
		}
	})

Is it possible to force a table event manually?

I tried this:

	if Asset == nil {
		func() ui.TableAction {
			event := ui.TableEvent{Action: ui.TableAction(ui.TableActionNew)}
			return event.Action
		}()
	}

I was hoping that if Asset didn't have data upon opening the table, before the data was populated and what not that I could trigger the TableActionNew event. I tried to use ui.SendEventToChild() but it seems that table actions are not the right kind of action to be able to send that.

from clui.

MostHated avatar MostHated commented on August 25, 2024

I can't seem to find a way to redraw the table with current data. I read that things like OnBeforeDraw and OnDrawCell are callbacks, but I am not sure what is actually telling the table to draw a new row. I add data to dbCache (as well as my tmpAssetData), but I always have to save (which takes the data from tmpAssetData and writes it to AssetData) close the table and reopen it (which then takes any data that is in AssetData and puts it into tmpAssetData, which then the table uses for its data to draw with initially.

So when I create a new row, I create it in dbCache and tmpAssetData, but nothing triggers for it to redraw with the newly added row. I tried to manually call Draw(), td.OnBeforeDraw(), td.OnDrawCell(), cache.preload() separetly, just to see if it would add the new column after I use ui.TableActionNew but I am stumped on how to do it without closing and reopening the window.

Is there a way somehow in dlg.OnClose(func()) I can force it to happen once I add my new row? I could technically force the entire table window to close and reopen, but that would require saving the new row from tmpAssetData to AssetData automatically so that when the table opens again it can load the new row back into tmpAssetData because that gets wiped out when the table closes. Saving it automatically and opening/closing again quickly would defeat the purpose of having a manual save button, and storing values in tmpAssetData (which is in case the user adds a row, but then decides to cancel the action, they have to save manually with the button)

from clui.

MostHated avatar MostHated commented on August 25, 2024

Looks like that did the job. Thanks!

from clui.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.