a bunch of refactors and allowing data from one tab to be reused in others.

This commit is contained in:
fiatjaf
2025-11-29 17:01:23 -03:00
parent 3b9ef7ddb2
commit 51ed4d7cfc
4 changed files with 302 additions and 114 deletions
+65 -15
View File
@@ -10,10 +10,14 @@ import (
"github.com/mappu/miqt/qt6/mainthread"
)
var event struct {
var event = &eventVars{}
type eventVars struct {
kindSpin *qt.QSpinBox
kindNameLabel *qt.QLabel
tagRows [][]*qt.QLineEdit
tagRowHBoxes []*qt.QHBoxLayout
tagsLayout *qt.QVBoxLayout
contentEdit *qt.QTextEdit
createdAtEdit *qt.QDateTimeEdit
outputEdit *qt.QTextEdit
@@ -41,7 +45,7 @@ func setupEventTab() *qt.QWidget {
event.kindSpin.SetMaximum(1<<16 - 1)
kindHBox.AddWidget(event.kindSpin.QWidget)
event.kindSpin.OnValueChanged(func(int) {
updateEvent()
event.updateEvent()
})
event.kindNameLabel = qt.NewQLabel2()
kindHBox.AddWidget(event.kindNameLabel.QWidget)
@@ -52,7 +56,7 @@ func setupEventTab() *qt.QWidget {
layout.AddWidget(contentLabel.QWidget)
event.contentEdit = qt.NewQTextEdit(tab)
layout.AddWidget(event.contentEdit.QWidget)
event.contentEdit.OnTextChanged(updateEvent)
event.contentEdit.OnTextChanged(event.updateEvent)
// created_at input
createdAtLabel := qt.NewQLabel2()
@@ -62,23 +66,23 @@ func setupEventTab() *qt.QWidget {
event.createdAtEdit.SetDateTime(qt.QDateTime_CurrentDateTime())
layout.AddWidget(event.createdAtEdit.QWidget)
event.createdAtEdit.OnDateTimeChanged(func(*qt.QDateTime) {
updateEvent()
event.updateEvent()
})
// tags input
tagsLabel := qt.NewQLabel2()
tagsLabel.SetText("tags:")
layout.AddWidget(tagsLabel.QWidget)
tagsLayout := qt.NewQVBoxLayout2()
tagRowHBoxes := make([]*qt.QHBoxLayout, 0, 2)
event.tagsLayout = qt.NewQVBoxLayout2()
event.tagRowHBoxes = make([]*qt.QHBoxLayout, 0, 2)
event.tagRows = make([][]*qt.QLineEdit, 0, 2)
layout.AddLayout(tagsLayout.QLayout)
layout.AddLayout(event.tagsLayout.QLayout)
var addTagRow func()
addTagRow = func() {
hbox := qt.NewQHBoxLayout2()
tagRowHBoxes = append(tagRowHBoxes, hbox)
tagsLayout.AddLayout(hbox.QLayout)
event.tagRowHBoxes = append(event.tagRowHBoxes, hbox)
event.tagsLayout.AddLayout(hbox.QLayout)
tagItems := []*qt.QLineEdit{}
y := len(event.tagRows)
event.tagRows = append(event.tagRows, tagItems)
@@ -115,16 +119,16 @@ func setupEventTab() *qt.QWidget {
itemIsFilled := func(edit *qt.QLineEdit) bool { return strings.TrimSpace(edit.Text()) != "" }
if nRows >= 2 && !slices.ContainsFunc(event.tagRows[nRows-1], itemIsFilled) && !slices.ContainsFunc(event.tagRows[nRows-2], itemIsFilled) {
// remove the last row if the last 2 are empty
tagsLayout.RemoveItem(tagRowHBoxes[nRows-1].QLayoutItem)
event.tagsLayout.RemoveItem(event.tagRowHBoxes[nRows-1].QLayoutItem)
event.tagRowHBoxes[nRows-1].DeleteLater()
for _, tagItem := range event.tagRows[nRows-1] {
tagItem.DeleteLater()
}
tagRowHBoxes[nRows-1].DeleteLater()
event.tagRows = event.tagRows[0 : nRows-1]
tagRowHBoxes = tagRowHBoxes[0 : nRows-1]
event.tagRowHBoxes = event.tagRowHBoxes[0 : nRows-1]
}
}
updateEvent()
event.updateEvent()
})
}
addItem()
@@ -137,9 +141,17 @@ func setupEventTab() *qt.QWidget {
outputLabel := qt.NewQLabel2()
outputLabel.SetText("event:")
layout.AddWidget(outputLabel.QWidget)
outputHBox := qt.NewQHBoxLayout2()
layout.AddLayout(outputHBox.QLayout)
event.outputEdit = qt.NewQTextEdit(tab)
event.outputEdit.SetReadOnly(true)
layout.AddWidget(event.outputEdit.QWidget)
outputHBox.AddWidget(event.outputEdit.QWidget)
copyButton := qt.NewQPushButton5("➡️", tab)
outputHBox.AddWidget(copyButton.QWidget)
copyButton.OnClicked(func() {
tabWidget.SetCurrentIndex(tabIndexes.paste)
paste.inputEdit.SetPlainText(event.outputEdit.ToPlainText())
})
// send button
buttonHBox := qt.NewQHBoxLayout2()
@@ -241,7 +253,7 @@ func setupEventTab() *qt.QWidget {
return tab
}
func updateEvent() {
func (event *eventVars) updateEvent() {
kind := nostr.Kind(event.kindSpin.Value())
kindName := kind.Name()
if kindName != "unknown" {
@@ -309,3 +321,41 @@ func updateEvent() {
finalize()
}
func (event *eventVars) populate(evt nostr.Event) {
// populate event tab
event.kindSpin.SetValue(int(evt.Kind))
event.contentEdit.SetPlainText(evt.Content)
// created_at
dt := qt.NewQDateTime()
dt.SetMSecsSinceEpoch(int64(evt.CreatedAt) * 1000)
event.createdAtEdit.SetDateTime(dt)
// tags
// clear all tag items and rows
for _, hbox := range event.tagRowHBoxes {
for _, item := range event.tagRows[len(event.tagRows)-1] {
item.DeleteLater()
}
hbox.DeleteLater()
}
event.tagRows = event.tagRows[:0]
event.tagRowHBoxes = event.tagRowHBoxes[:0]
// add tag rows and items from evt
for _, tag := range evt.Tags {
hbox := qt.NewQHBoxLayout2()
event.tagRowHBoxes = append(event.tagRowHBoxes, hbox)
event.tagsLayout.AddLayout(hbox.QLayout)
tagItems := []*qt.QLineEdit{}
for _, value := range tag {
edit := qt.NewQLineEdit(nil)
edit.SetText(value)
hbox.AddWidget(edit.QWidget)
tagItems = append(tagItems, edit)
}
event.tagRows = append(event.tagRows, tagItems)
}
event.updateEvent()
}
+26 -14
View File
@@ -23,12 +23,19 @@ var (
currentSec nostr.SecretKey
currentKeyer nostr.Keyer
statusLabel *qt.QLabel
debounced = debouncer.New(950 * time.Millisecond)
sys = sdk.NewSystem()
ctx = context.Background()
debug = flag.Bool("debug", false, "enable debug mode")
initialTab = flag.String("tab", "paste", "tab to open initially")
tabIndexes struct {
event int
req int
paste int
}
statusLabel *qt.QLabel
debounced = debouncer.New(950 * time.Millisecond)
sys = sdk.NewSystem()
ctx = context.Background()
debug = flag.Bool("debug", false, "enable debug mode")
initialTab = flag.String("tab", "paste", "tab to open initially")
)
func main() {
@@ -114,7 +121,7 @@ func main() {
currentSec = sk
currentKeyer = keyer
statusLabel.SetText("")
updateEvent()
event.updateEvent()
return
empty:
@@ -139,18 +146,23 @@ func main() {
pasteTab := setupPasteTab()
tabWidget.AddTab(eventTab, "event")
tabIndexes.event = 0
tabWidget.AddTab(reqTab, "req")
tabIndexes.req = 1
tabWidget.AddTab(pasteTab, "paste")
tabIndexes.paste = 2
switch *initialTab {
case "event":
tabWidget.SetCurrentIndex(0)
tabWidget.SetCurrentIndex(tabIndexes.event)
case "req":
tabWidget.SetCurrentIndex(1)
tabWidget.SetCurrentIndex(tabIndexes.req)
case "paste":
tabWidget.SetCurrentIndex(2)
tabWidget.SetCurrentIndex(tabIndexes.paste)
default:
tabWidget.SetCurrentIndex(2)
tabWidget.SetCurrentIndex(0)
}
mainLayout.AddWidget(tabWidget.QWidget)
@@ -159,9 +171,9 @@ func main() {
mainLayout.AddWidget(statusLabel.QWidget)
// initial render
updateEvent()
updateReq()
updatePaste()
event.updateEvent()
req.updateReq()
paste.updatePaste()
window.Show()
qt.QApplication_Exec()
+12 -73
View File
@@ -24,7 +24,7 @@ type pasteVars struct {
nip05ctxAbort error
}
var paste = pasteVars{
var paste = &pasteVars{
nip05ctxAbort: errors.New("aborted"),
}
@@ -39,7 +39,7 @@ func setupPasteTab() *qt.QWidget {
layout.AddWidget(inputLabel.QWidget)
paste.inputEdit = qt.NewQTextEdit(tab)
layout.AddWidget(paste.inputEdit.QWidget)
paste.inputEdit.OnTextChanged(updatePaste)
paste.inputEdit.OnTextChanged(paste.updatePaste)
// output
paste.outputVBox = qt.NewQVBoxLayout2()
@@ -61,7 +61,7 @@ func deleteLayoutRecursively(layout *qt.QLayout) {
layout.DeleteLater()
}
func updatePaste() {
func (paste *pasteVars) updatePaste() {
// clear previous output
for paste.outputVBox.Count() > 0 {
item := paste.outputVBox.ItemAt(0)
@@ -73,6 +73,7 @@ func updatePaste() {
paste.outputVBox.RemoveItem(item)
}
// process current
text := strings.TrimSpace(paste.inputEdit.ToPlainText())
if text == "" {
return
@@ -96,7 +97,7 @@ func updatePaste() {
// try JSON event
var event nostr.Event
if err := json.Unmarshal([]byte(text), &event); err == nil {
if err := json.Unmarshal([]byte(text), &event); err == nil && (event.ID != nostr.ZeroID || event.Kind != 0 || event.CreatedAt != 0 || event.Content != "" || event.Tags != nil || event.PubKey != nostr.ZeroPK) {
paste.displayEventButton(event)
return
}
@@ -323,83 +324,21 @@ func (p *pasteVars) displayNip05(identifier string) {
}
func (p *pasteVars) displayEventButton(evt nostr.Event) {
button := qt.NewQPushButton5("event", window.QWidget)
button := qt.NewQPushButton5("event ➡️", window.QWidget)
button.OnClicked(func() {
// populate event tab
event.kindSpin.SetValue(int(evt.Kind))
event.contentEdit.SetPlainText(evt.Content)
// created_at
dt := qt.NewQDateTime()
dt.SetMSecsSinceEpoch(int64(evt.CreatedAt) * 1000)
event.createdAtEdit.SetDateTime(dt)
// tags
// clear existing
for len(event.tagRows) > 1 {
// remove last row
lastRow := event.tagRows[len(event.tagRows)-1]
for _, edit := range lastRow {
edit.DeleteLater()
}
event.tagRows = event.tagRows[:len(event.tagRows)-1]
}
// set tags
for i, tag := range evt.Tags {
if i >= len(event.tagRows) {
// add more rows if needed
// but for simplicity, just set what we can
break
}
for j, value := range tag {
if j >= len(event.tagRows[i]) {
break
}
event.tagRows[i][j].SetText(value)
}
}
event.populate(evt)
// switch to event tab
tabWidget.SetCurrentIndex(0) // assuming event is first
tabWidget.SetCurrentIndex(tabIndexes.event)
})
p.outputVBox.AddWidget(button.QWidget)
}
func (p *pasteVars) displayFilterButton(filt nostr.Filter) {
button := qt.NewQPushButton5("filter", window.QWidget)
func (p *pasteVars) displayFilterButton(filter nostr.Filter) {
button := qt.NewQPushButton5("filter ➡️", window.QWidget)
button.OnClicked(func() {
// populate req tab
// authors
for i, author := range filt.Authors {
if i < len(req.authorsEdits) {
req.authorsEdits[i].SetText(nip19.EncodeNpub(author))
}
}
// ids
for i, id := range filt.IDs {
if i < len(req.idsEdits) {
req.idsEdits[i].SetText(id.String())
}
}
// kinds
for i, kind := range filt.Kinds {
if i < len(req.kindsEdits) {
req.kindsEdits[i].SetText(fmt.Sprintf("%d", kind))
}
}
// since
if filt.Since != 0 {
dt := qt.NewQDateTime()
dt.SetMSecsSinceEpoch(int64(filt.Since) * 1000)
req.sinceEdit.SetDateTime(dt)
}
// until
if filt.Until != 0 {
dt := qt.NewQDateTime()
dt.SetMSecsSinceEpoch(int64(filt.Until) * 1000)
req.untilEdit.SetDateTime(dt)
}
// limit
req.limitSpin.SetValue(filt.Limit)
req.populate(filter)
// switch to req tab
tabWidget.SetCurrentIndex(1) // assuming req is second
tabWidget.SetCurrentIndex(tabIndexes.req)
})
p.outputVBox.AddWidget(button.QWidget)
}
+199 -12
View File
@@ -17,6 +17,9 @@ type reqVars struct {
idsEdits []*qt.QLineEdit
kindsEdits []*qt.QLineEdit
kindsLabels []*qt.QLabel
tagRows [][]*qt.QLineEdit
tagRowHBoxes []*qt.QHBoxLayout
tagsLayout *qt.QVBoxLayout
relaysEdits []*qt.QLineEdit
sinceEdit *qt.QDateTimeEdit
sinceCheck *qt.QCheckBox
@@ -31,7 +34,7 @@ type reqVars struct {
resultsList *qt.QListWidget
}
var req = reqVars{}
var req = &reqVars{}
func setupReqTab() *qt.QWidget {
tab := qt.NewQWidget(window.QWidget)
@@ -63,7 +66,7 @@ func setupReqTab() *qt.QWidget {
req.authorsEdits = req.authorsEdits[0 : n-1]
}
}
updateReq()
req.updateReq()
})
}
addAuthorEdit()
@@ -93,7 +96,7 @@ func setupReqTab() *qt.QWidget {
req.idsEdits = req.idsEdits[0 : n-1]
}
}
updateReq()
req.updateReq()
})
}
addIdEdit()
@@ -136,11 +139,67 @@ func setupReqTab() *qt.QWidget {
req.kindsLabels = req.kindsLabels[0 : n-1]
}
}
updateReq()
req.updateReq()
})
}
addKindEdit()
// tags
tagsLabel := qt.NewQLabel2()
tagsLabel.SetText("tags:")
layout.AddWidget(tagsLabel.QWidget)
req.tagsLayout = qt.NewQVBoxLayout2()
layout.AddLayout(req.tagsLayout.QLayout)
req.tagRows = make([][]*qt.QLineEdit, 0, 2)
req.tagRowHBoxes = make([]*qt.QHBoxLayout, 0, 2)
var addTagRow func()
addTagRow = func() {
hbox := qt.NewQHBoxLayout2()
req.tagRowHBoxes = append(req.tagRowHBoxes, hbox)
req.tagsLayout.AddLayout(hbox.QLayout)
tagItems := []*qt.QLineEdit{}
y := len(req.tagRows)
req.tagRows = append(req.tagRows, tagItems)
var addItem func()
addItem = func() {
edit := qt.NewQLineEdit(tab)
hbox.AddWidget(edit.QWidget)
x := len(tagItems)
tagItems = append(tagItems, edit)
req.tagRows[y] = tagItems
edit.OnTextChanged(func(text string) {
if strings.TrimSpace(text) != "" {
if y == len(req.tagRows)-1 && x == 0 {
addTagRow()
}
if x == len(tagItems)-1 {
addItem()
}
} else {
nItems := len(tagItems)
if nItems >= 2 && strings.TrimSpace(tagItems[nItems-1].Text()) == "" && strings.TrimSpace(tagItems[nItems-2].Text()) == "" {
hbox.RemoveWidget(tagItems[nItems-1].QWidget)
tagItems[nItems-1].DeleteLater()
tagItems = tagItems[0 : nItems-1]
req.tagRows[y] = tagItems
}
nRows := len(req.tagRows)
if nRows >= 2 && len(req.tagRows[nRows-1]) == 0 && len(req.tagRows[nRows-2]) == 0 {
req.tagsLayout.RemoveItem(hbox.QLayoutItem)
hbox.DeleteLater()
req.tagRows = req.tagRows[0 : nRows-1]
req.tagRowHBoxes = req.tagRowHBoxes[0 : nRows-1]
}
}
req.updateReq()
})
}
addItem()
}
addTagRow()
// since
sinceHBox := qt.NewQHBoxLayout2()
layout.AddLayout(sinceHBox.QLayout)
@@ -165,10 +224,10 @@ func setupReqTab() *qt.QWidget {
qtime.SetMSecsSinceEpoch(time.Now().UnixMilli())
req.sinceEdit.SetDateTime(qtime)
}
updateReq()
req.updateReq()
})
req.sinceEdit.OnDateTimeChanged(func(*qt.QDateTime) {
updateReq()
req.updateReq()
})
// until
@@ -195,10 +254,10 @@ func setupReqTab() *qt.QWidget {
qtime.SetMSecsSinceEpoch(time.Now().UnixMilli())
req.untilEdit.SetDateTime(qtime)
}
updateReq()
req.updateReq()
})
req.untilEdit.OnDateTimeChanged(func(*qt.QDateTime) {
updateReq()
req.updateReq()
})
// limit
@@ -217,10 +276,10 @@ func setupReqTab() *qt.QWidget {
limitHBox.AddWidget(req.limitSpin.QWidget)
req.limitCheck.OnStateChanged(func(state int) {
req.limitSpin.SetEnabled(state == 2) // 2 is checked
updateReq()
req.updateReq()
})
req.limitSpin.OnValueChanged(func(int) {
updateReq()
req.updateReq()
})
// output
@@ -308,7 +367,7 @@ func setupReqTab() *qt.QWidget {
return tab
}
func updateReq() {
func (req *reqVars) updateReq() {
req.filter = nostr.Filter{}
// collect authors
@@ -345,6 +404,28 @@ func updateReq() {
req.filter.Kinds = kinds
}
// collect tags
tags := make(map[string][]string)
for _, tagItems := range req.tagRows {
if len(tagItems) == 0 || strings.TrimSpace(tagItems[0].Text()) == "" {
continue
}
key := strings.TrimSpace(tagItems[0].Text())
values := []string{}
for _, edit := range tagItems[1:] {
text := strings.TrimSpace(edit.Text())
if text != "" {
values = append(values, text)
}
}
if len(values) > 0 {
tags[key] = values
}
}
if len(tags) > 0 {
req.filter.Tags = tags
}
// update kind labels
for i, kind := range kinds {
if i < len(req.kindsLabels) {
@@ -385,7 +466,7 @@ func updateReq() {
req.outputEdit.SetPlainText(string(jsonBytes))
}
func (req reqVars) subscribe() {
func (req *reqVars) subscribe() {
// collect relays
relays := []string{}
for _, edit := range req.relaysEdits {
@@ -468,3 +549,109 @@ func (req reqVars) subscribe() {
eosed = true
}()
}
func (req *reqVars) populate(filter nostr.Filter) {
// clear all authors, kinds, ids fields
// For simplicity, clear all and add new ones
// But since the UI is dynamic, perhaps set the first one and clear others
// For authors
if len(filter.Authors) > 0 {
req.authorsEdits[0].SetText(filter.Authors[0].String())
for i := 1; i < len(req.authorsEdits); i++ {
req.authorsEdits[i].SetText("")
}
} else {
for _, edit := range req.authorsEdits {
edit.SetText("")
}
}
// For ids
if len(filter.IDs) > 0 {
req.idsEdits[0].SetText(filter.IDs[0].String())
for i := 1; i < len(req.idsEdits); i++ {
req.idsEdits[i].SetText("")
}
} else {
for _, edit := range req.idsEdits {
edit.SetText("")
}
}
// For kinds
if len(filter.Kinds) > 0 {
req.kindsEdits[0].SetText(strconv.Itoa(int(filter.Kinds[0])))
for i := 1; i < len(req.kindsEdits); i++ {
if i < len(filter.Kinds) {
req.kindsEdits[i].SetText(strconv.Itoa(int(filter.Kinds[i])))
} else {
req.kindsEdits[i].SetText("")
}
}
} else {
for _, edit := range req.kindsEdits {
edit.SetText("")
}
}
// clear all tag items and rows
for _, hbox := range req.tagRowHBoxes {
for _, item := range req.tagRows[len(req.tagRows)-1] {
item.DeleteLater()
}
hbox.DeleteLater()
}
req.tagRows = req.tagRows[:0]
req.tagRowHBoxes = req.tagRowHBoxes[:0]
// add tags according to filter
for key, values := range filter.Tags {
hbox := qt.NewQHBoxLayout2()
req.tagRowHBoxes = append(req.tagRowHBoxes, hbox)
req.tagsLayout.AddLayout(hbox.QLayout)
tagItems := []*qt.QLineEdit{}
edit := qt.NewQLineEdit(nil)
edit.SetText(key)
hbox.AddWidget(edit.QWidget)
tagItems = append(tagItems, edit)
for _, value := range values {
edit := qt.NewQLineEdit(nil)
edit.SetText(value)
hbox.AddWidget(edit.QWidget)
tagItems = append(tagItems, edit)
}
req.tagRows = append(req.tagRows, tagItems)
}
// set since, until, limit
if filter.Since != 0 {
req.sinceCheck.SetChecked(true)
dt := qt.NewQDateTime()
dt.SetMSecsSinceEpoch(int64(filter.Since) * 1000)
req.sinceEdit.SetDateTime(dt)
} else {
req.sinceCheck.SetChecked(false)
}
if filter.Until != 0 {
req.untilCheck.SetChecked(true)
dt := qt.NewQDateTime()
dt.SetMSecsSinceEpoch(int64(filter.Until) * 1000)
req.untilEdit.SetDateTime(dt)
} else {
req.untilCheck.SetChecked(false)
}
if filter.Limit != 0 {
req.limitCheck.SetChecked(true)
req.limitSpin.SetValue(filter.Limit)
} else if filter.LimitZero {
req.limitCheck.SetChecked(true)
req.limitSpin.SetValue(0)
} else {
req.limitCheck.SetChecked(false)
}
req.updateReq()
}