2024-09-20 06:39:01 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-09-28 01:04:31 +00:00
|
|
|
"fmt"
|
2024-09-20 06:39:01 +00:00
|
|
|
"go/format"
|
2024-09-28 01:04:31 +00:00
|
|
|
"strconv"
|
2024-09-20 06:39:01 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2024-10-03 05:07:43 +00:00
|
|
|
var (
|
2024-10-05 04:49:21 +00:00
|
|
|
RootWindowName = ""
|
2024-10-03 05:07:43 +00:00
|
|
|
DefaultGridMargin = 11
|
|
|
|
DefaultSpacing = 6
|
2024-10-03 05:51:50 +00:00
|
|
|
IconCounter = 0
|
2024-10-03 05:07:43 +00:00
|
|
|
)
|
|
|
|
|
2024-10-03 04:11:30 +00:00
|
|
|
func collectClassNames_Widget(u *UiWidget) []string {
|
2024-09-20 06:39:01 +00:00
|
|
|
var ret []string
|
|
|
|
if u.Name != "" {
|
|
|
|
ret = append(ret, u.Name+" *qt."+u.Class)
|
|
|
|
}
|
|
|
|
for _, w := range u.Widgets {
|
2024-10-03 04:11:30 +00:00
|
|
|
ret = append(ret, collectClassNames_Widget(&w)...)
|
2024-09-20 06:39:01 +00:00
|
|
|
}
|
|
|
|
if u.Layout != nil {
|
2024-09-28 01:04:31 +00:00
|
|
|
ret = append(ret, u.Layout.Name+" *qt."+u.Layout.Class)
|
2024-09-20 06:39:01 +00:00
|
|
|
for _, li := range u.Layout.Items {
|
2024-10-03 04:11:30 +00:00
|
|
|
if li.Widget != nil {
|
|
|
|
ret = append(ret, collectClassNames_Widget(li.Widget)...)
|
|
|
|
}
|
|
|
|
if li.Spacer != nil {
|
|
|
|
ret = append(ret, li.Spacer.Name+" *qt.QSpacerItem")
|
|
|
|
}
|
2024-09-20 06:39:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, a := range u.Actions {
|
|
|
|
ret = append(ret, a.Name+" *qt.QAction")
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
func generateString(s *UiString, parentClass string) string {
|
|
|
|
if s.Notr || parentClass == "" {
|
2024-09-28 01:04:31 +00:00
|
|
|
return strconv.Quote(s.Value)
|
2024-09-20 06:39:01 +00:00
|
|
|
}
|
2024-09-28 02:27:19 +00:00
|
|
|
return `qt.` + parentClass + `_Tr(` + strconv.Quote(s.Value) + `)`
|
2024-09-28 01:04:31 +00:00
|
|
|
}
|
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
// qwidgetName creates the T.QWidget name that MIQT needs to access the base class.
|
|
|
|
func qwidgetName(name string, class string) string {
|
|
|
|
if name == "" {
|
|
|
|
return "nil"
|
|
|
|
}
|
|
|
|
if class == "QWidget" {
|
|
|
|
return name // It's already the right type
|
|
|
|
}
|
|
|
|
return name + ".QWidget"
|
|
|
|
}
|
|
|
|
|
2024-10-03 04:10:09 +00:00
|
|
|
func normalizeEnumName(s string) string {
|
|
|
|
if strings.HasPrefix(s, `Qt::`) {
|
|
|
|
s = s[4:]
|
|
|
|
}
|
|
|
|
|
|
|
|
return `qt.` + strings.Replace(s, `::`, `__`, -1)
|
|
|
|
}
|
|
|
|
|
2024-10-03 06:32:28 +00:00
|
|
|
func renderIcon(iconVal *UiIcon, ret *strings.Builder) string {
|
2024-10-03 05:51:50 +00:00
|
|
|
|
|
|
|
iconName := fmt.Sprintf("icon%d", IconCounter)
|
|
|
|
IconCounter++
|
|
|
|
|
|
|
|
ret.WriteString(iconName + " := qt.NewQIcon()\n")
|
|
|
|
|
|
|
|
// A base entry is a synonym for NormalOff. Don't need them both
|
|
|
|
if iconVal.NormalOff != nil {
|
|
|
|
ret.WriteString(iconName + ".AddFile4(" + strconv.Quote(*iconVal.NormalOff) + ", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__Off)\n")
|
|
|
|
} else {
|
|
|
|
ret.WriteString(iconName + ".AddFile(" + strconv.Quote(strings.TrimSpace(iconVal.Base)) + ")\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
if iconVal.NormalOn != nil {
|
|
|
|
ret.WriteString(iconName + ".AddFile4(" + strconv.Quote(*iconVal.NormalOn) + ", qt.NewQSize(), qt.QIcon__Normal, qt.QIcon__On)\n")
|
|
|
|
}
|
|
|
|
if iconVal.ActiveOff != nil {
|
|
|
|
ret.WriteString(iconName + ".AddFile4(" + strconv.Quote(*iconVal.NormalOn) + ", qt.NewQSize(), qt.QIcon__Active, qt.QIcon__Off)\n")
|
|
|
|
}
|
|
|
|
if iconVal.ActiveOn != nil {
|
|
|
|
ret.WriteString(iconName + ".AddFile4(" + strconv.Quote(*iconVal.NormalOn) + ", qt.NewQSize(), qt.QIcon__Active, qt.QIcon__On)\n")
|
|
|
|
}
|
|
|
|
if iconVal.DisabledOff != nil {
|
|
|
|
ret.WriteString(iconName + ".AddFile4(" + strconv.Quote(*iconVal.NormalOn) + ", qt.NewQSize(), qt.QIcon__Disabled, qt.QIcon__Off)\n")
|
|
|
|
}
|
|
|
|
if iconVal.DisabledOn != nil {
|
|
|
|
ret.WriteString(iconName + ".AddFile4(" + strconv.Quote(*iconVal.NormalOn) + ", qt.NewQSize(), qt.QIcon__Disabled, qt.QIcon__On)\n")
|
|
|
|
}
|
|
|
|
if iconVal.SelectedOff != nil {
|
|
|
|
ret.WriteString(iconName + ".AddFile4(" + strconv.Quote(*iconVal.NormalOn) + ", qt.NewQSize(), qt.QIcon__Selected, qt.QIcon__Off)\n")
|
|
|
|
}
|
|
|
|
if iconVal.SelectedOn != nil {
|
|
|
|
ret.WriteString(iconName + ".AddFile4(" + strconv.Quote(*iconVal.NormalOn) + ", qt.NewQSize(), qt.QIcon__Selected, qt.QIcon__On)\n")
|
|
|
|
}
|
|
|
|
|
2024-10-03 06:32:28 +00:00
|
|
|
return iconName
|
2024-10-03 05:51:50 +00:00
|
|
|
}
|
|
|
|
|
2024-10-03 05:07:43 +00:00
|
|
|
func renderProperties(properties []UiProperty, ret *strings.Builder, targetName, parentClass string, isLayout bool) error {
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-10-03 05:07:43 +00:00
|
|
|
contentsMargins := [4]int{DefaultGridMargin, DefaultGridMargin, DefaultGridMargin, DefaultGridMargin} // left, top, right, bottom
|
|
|
|
customContentsMargins := false
|
|
|
|
customSpacing := false
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-10-03 05:07:43 +00:00
|
|
|
for _, prop := range properties {
|
2024-09-28 01:04:31 +00:00
|
|
|
setterFunc := `.Set` + strings.ToUpper(string(prop.Name[0])) + prop.Name[1:]
|
|
|
|
|
|
|
|
if prop.Name == "geometry" {
|
|
|
|
if !(prop.RectVal.X == 0 && prop.RectVal.Y == 0) {
|
|
|
|
// Set all 4x properties
|
2024-10-03 05:07:43 +00:00
|
|
|
ret.WriteString(`ui.` + targetName + `.SetGeometry(qt.NewQRect(` + fmt.Sprintf("%d, %d, %d, %d", prop.RectVal.X, prop.RectVal.Y, prop.RectVal.Width, prop.RectVal.Height) + "))\n")
|
2024-09-28 01:04:31 +00:00
|
|
|
|
|
|
|
} else if !(prop.RectVal.Width == 0 && prop.RectVal.Height == 0) {
|
|
|
|
// Only width/height were supplied
|
2024-10-03 05:07:43 +00:00
|
|
|
ret.WriteString(`ui.` + targetName + `.Resize(` + fmt.Sprintf("%d, %d", prop.RectVal.Width, prop.RectVal.Height) + ")\n")
|
2024-09-28 01:04:31 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-10-03 05:07:43 +00:00
|
|
|
} else if prop.Name == "leftMargin" {
|
|
|
|
contentsMargins[0] = mustParseInt(*prop.NumberVal)
|
|
|
|
customContentsMargins = true
|
|
|
|
|
|
|
|
} else if prop.Name == "topMargin" {
|
|
|
|
contentsMargins[1] = mustParseInt(*prop.NumberVal)
|
|
|
|
customContentsMargins = true
|
|
|
|
|
|
|
|
} else if prop.Name == "rightMargin" {
|
|
|
|
contentsMargins[2] = mustParseInt(*prop.NumberVal)
|
|
|
|
customContentsMargins = true
|
|
|
|
|
|
|
|
} else if prop.Name == "bottomMargin" {
|
|
|
|
contentsMargins[3] = mustParseInt(*prop.NumberVal)
|
|
|
|
customContentsMargins = true
|
|
|
|
|
2024-09-28 01:04:31 +00:00
|
|
|
} else if prop.StringVal != nil {
|
|
|
|
// "windowTitle", "title", "text"
|
2024-10-03 05:07:43 +00:00
|
|
|
ret.WriteString(`ui.` + targetName + setterFunc + `(` + generateString(prop.StringVal, parentClass) + ")\n")
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-10-03 04:23:12 +00:00
|
|
|
} else if prop.NumberVal != nil {
|
|
|
|
// "currentIndex"
|
2024-10-03 05:07:43 +00:00
|
|
|
if prop.Name == "spacing" {
|
|
|
|
customSpacing = true
|
|
|
|
}
|
|
|
|
ret.WriteString(`ui.` + targetName + setterFunc + `(` + *prop.NumberVal + ")\n")
|
2024-10-03 04:23:12 +00:00
|
|
|
|
|
|
|
} else if prop.BoolVal != nil {
|
|
|
|
// "childrenCollapsible"
|
2024-10-03 05:07:43 +00:00
|
|
|
ret.WriteString(`ui.` + targetName + setterFunc + `(` + formatBool(*prop.BoolVal) + ")\n")
|
2024-10-03 04:23:12 +00:00
|
|
|
|
2024-09-28 01:04:31 +00:00
|
|
|
} else if prop.EnumVal != nil {
|
|
|
|
// "frameShape"
|
2024-10-03 04:10:09 +00:00
|
|
|
|
|
|
|
// Newer versions of Qt Designer produce the fully qualified enum
|
|
|
|
// names (A::B::C) but miqt changed to use the short names. Need to
|
|
|
|
// detect the case and convert it to match
|
2024-10-03 05:07:43 +00:00
|
|
|
ret.WriteString(`ui.` + targetName + setterFunc + `(` + normalizeEnumName(*prop.EnumVal) + ")\n")
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-10-05 04:48:58 +00:00
|
|
|
} else if prop.SetVal != nil {
|
|
|
|
// QDialogButtonBox::"standardButtons"
|
|
|
|
// <set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
|
|
|
|
|
|
|
|
parts := strings.Split(*prop.SetVal, `|`)
|
|
|
|
for i, p := range parts {
|
|
|
|
parts[i] = normalizeEnumName(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
emit := "0"
|
|
|
|
if len(parts) > 0 {
|
|
|
|
emit = strings.Join(parts, `|`)
|
|
|
|
}
|
|
|
|
ret.WriteString(`ui.` + targetName + setterFunc + `(` + emit + ")\n")
|
|
|
|
|
2024-10-03 05:51:50 +00:00
|
|
|
} else if prop.IconVal != nil {
|
2024-10-03 06:32:28 +00:00
|
|
|
iconName := renderIcon(prop.IconVal, ret)
|
|
|
|
ret.WriteString(`ui.` + targetName + setterFunc + `(` + iconName + ")\n")
|
2024-10-03 05:51:50 +00:00
|
|
|
|
2024-09-28 01:04:31 +00:00
|
|
|
} else {
|
2024-10-03 05:07:43 +00:00
|
|
|
ret.WriteString("/* miqt-uic: no handler for " + targetName + " property '" + prop.Name + "' */\n")
|
2024-09-28 01:04:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-03 05:07:43 +00:00
|
|
|
if customContentsMargins || isLayout {
|
|
|
|
ret.WriteString(`ui.` + targetName + `.SetContentsMargins(` + fmt.Sprintf("%d, %d, %d, %d", contentsMargins[0], contentsMargins[1], contentsMargins[2], contentsMargins[3]) + ")\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !customSpacing && isLayout {
|
|
|
|
// Layouts must specify spacing, unless, we specified it already
|
|
|
|
ret.WriteString(`ui.` + targetName + `.SetSpacing(` + fmt.Sprintf("%d", DefaultSpacing) + ")\n")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateWidget(w UiWidget, parentName string, parentClass string) (string, error) {
|
|
|
|
ret := strings.Builder{}
|
|
|
|
|
|
|
|
ctor := constructorFunctionFor(w.Class)
|
|
|
|
|
|
|
|
ret.WriteString(`
|
|
|
|
ui.` + w.Name + ` = qt.` + ctor + `(` + qwidgetName(parentName, parentClass) + `)
|
|
|
|
ui.` + w.Name + `.SetObjectName(` + strconv.Quote(w.Name) + `)
|
|
|
|
`)
|
2024-10-05 04:49:21 +00:00
|
|
|
if RootWindowName == "" {
|
|
|
|
RootWindowName = `ui.` + w.Name
|
|
|
|
}
|
2024-10-03 05:07:43 +00:00
|
|
|
|
|
|
|
// Properties
|
|
|
|
|
|
|
|
err := renderProperties(w.Properties, &ret, w.Name, parentClass, false)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
// Attributes
|
2024-09-28 02:39:56 +00:00
|
|
|
|
|
|
|
for _, attr := range w.Attributes {
|
|
|
|
if parentClass == "QTabWidget" && attr.Name == "title" {
|
|
|
|
ret.WriteString(parentName + `.SetTabText(` + parentName + ".IndexOf(ui." + w.Name + "), " + generateString(attr.StringVal, parentClass) + ")\n")
|
|
|
|
|
|
|
|
} else if w.Class == "QDockWidget" && parentClass == "QMainWindow" && attr.Name == "dockWidgetArea" {
|
|
|
|
ret.WriteString(parentName + `.AddDockWidget(qt.DockWidgetArea(` + *attr.NumberVal + `), ui.` + w.Name + `)` + "\n")
|
|
|
|
|
2024-10-03 05:07:43 +00:00
|
|
|
} else if w.Class == "QToolBar" && parentClass == "QMainWindow" && attr.Name == "toolBarArea" {
|
|
|
|
ret.WriteString(parentName + `.AddToolBar(` + normalizeEnumName(*attr.EnumVal) + `, ui.` + w.Name + `)` + "\n")
|
|
|
|
|
2024-10-03 06:32:28 +00:00
|
|
|
} else if parentClass == "QTabWidget" && attr.Name == "icon" {
|
|
|
|
// This will be handled when we call .AddTab() on the parent QTabWidget
|
|
|
|
|
2024-09-28 02:39:56 +00:00
|
|
|
} else {
|
|
|
|
ret.WriteString("/* miqt-uic: no handler for " + w.Name + " attribute '" + attr.Name + "' */\n")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2024-09-28 02:27:19 +00:00
|
|
|
|
2024-09-28 01:04:31 +00:00
|
|
|
// Layout
|
2024-10-03 05:07:43 +00:00
|
|
|
|
2024-09-28 01:04:31 +00:00
|
|
|
if w.Layout != nil {
|
2024-10-03 04:22:49 +00:00
|
|
|
ctor := constructorFunctionFor(w.Layout.Class)
|
2024-09-28 02:27:19 +00:00
|
|
|
|
2024-09-28 01:04:31 +00:00
|
|
|
ret.WriteString(`
|
2024-09-28 02:27:19 +00:00
|
|
|
ui.` + w.Layout.Name + ` = qt.` + ctor + `(` + qwidgetName("ui."+w.Name, w.Class) + `)
|
2024-09-28 01:04:31 +00:00
|
|
|
ui.` + w.Layout.Name + `.SetObjectName(` + strconv.Quote(w.Layout.Name) + `)
|
|
|
|
`)
|
|
|
|
|
2024-10-03 05:07:43 +00:00
|
|
|
// Layout->Properties
|
|
|
|
|
|
|
|
err := renderProperties(w.Layout.Properties, &ret, w.Layout.Name, parentClass, true) // Always emit spacing/padding calls
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Layout->Items
|
|
|
|
|
2024-10-03 04:11:30 +00:00
|
|
|
for i, child := range w.Layout.Items {
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-10-03 04:11:30 +00:00
|
|
|
// A layout item is either a widget, or a spacer
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-10-03 04:11:30 +00:00
|
|
|
if child.Spacer != nil {
|
|
|
|
ret.WriteString("/* miqt-uic: no handler for spacer */\n")
|
2024-09-28 01:04:31 +00:00
|
|
|
}
|
|
|
|
|
2024-10-03 04:11:30 +00:00
|
|
|
if child.Widget != nil {
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-10-03 04:11:30 +00:00
|
|
|
// Layout items have the parent as the real QWidget parent and are
|
|
|
|
// separately assigned to the layout afterwards
|
2024-09-28 02:27:19 +00:00
|
|
|
|
2024-10-03 04:11:30 +00:00
|
|
|
nest, err := generateWidget(*child.Widget, `ui.`+w.Name, w.Class)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf(w.Name+"/Layout/Item[%d]: %w", i, err)
|
|
|
|
}
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-10-03 04:11:30 +00:00
|
|
|
ret.WriteString(nest)
|
|
|
|
|
|
|
|
// Assign to layout
|
|
|
|
|
|
|
|
switch w.Layout.Class {
|
|
|
|
case `QFormLayout`:
|
|
|
|
// Row and Column are always populated.
|
|
|
|
rowPos := fmt.Sprintf("%d", *child.Row)
|
|
|
|
var colPos string
|
|
|
|
if *child.Column == 0 {
|
|
|
|
colPos = `qt.QFormLayout__LabelRole`
|
|
|
|
} else if *child.Column == 1 {
|
|
|
|
colPos = `qt.QFormLayout__FieldRole`
|
|
|
|
} else {
|
|
|
|
ret.WriteString("/* miqt-uic: QFormLayout does not understand column index */\n")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// For QFormLayout it's SetWidget
|
|
|
|
ret.WriteString(`
|
|
|
|
ui.` + w.Layout.Name + `.SetWidget(` + rowPos + `, ` + colPos + `, ` + qwidgetName(`ui.`+child.Widget.Name, child.Widget.Class) + `)
|
|
|
|
`)
|
|
|
|
|
|
|
|
case `QGridLayout`:
|
2024-10-05 04:49:10 +00:00
|
|
|
if child.ColSpan != nil || child.RowSpan != nil {
|
|
|
|
// If either are present, use full four-value AddWidget3
|
|
|
|
rowSpan := 1
|
|
|
|
if child.RowSpan != nil {
|
|
|
|
rowSpan = *child.RowSpan
|
|
|
|
}
|
|
|
|
colSpan := 1
|
|
|
|
if child.ColSpan != nil {
|
|
|
|
colSpan = *child.ColSpan
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.WriteString(`
|
|
|
|
ui.` + w.Layout.Name + `.AddWidget3(` + qwidgetName(`ui.`+child.Widget.Name, child.Widget.Class) + `, ` + fmt.Sprintf("%d, %d, %d, %d", *child.Row, *child.Column, rowSpan, colSpan) + `)
|
|
|
|
`)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Row and Column are always present in the .ui file
|
|
|
|
// For row/column it's AddWidget2
|
|
|
|
|
|
|
|
ret.WriteString(`
|
2024-10-03 04:11:30 +00:00
|
|
|
ui.` + w.Layout.Name + `.AddWidget2(` + qwidgetName(`ui.`+child.Widget.Name, child.Widget.Class) + `, ` + fmt.Sprintf("%d, %d", *child.Row, *child.Column) + `)
|
|
|
|
`)
|
2024-10-05 04:49:10 +00:00
|
|
|
}
|
2024-10-03 04:11:30 +00:00
|
|
|
|
|
|
|
case "QVBoxLayout", "QHBoxLayout":
|
|
|
|
// For box layout it's AddWidget
|
|
|
|
ret.WriteString(`
|
|
|
|
ui.` + w.Layout.Name + `.AddWidget(` + qwidgetName(`ui.`+child.Widget.Name, child.Widget.Class) + `)
|
|
|
|
`)
|
|
|
|
|
|
|
|
default:
|
|
|
|
ret.WriteString("/* miqt-uic: no handler for layout '" + w.Layout.Class + "' */\n")
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-10-03 04:11:30 +00:00
|
|
|
}
|
2024-09-28 01:04:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Actions
|
|
|
|
|
|
|
|
for _, a := range w.Actions {
|
|
|
|
ret.WriteString(`
|
|
|
|
ui.` + a.Name + ` = qt.NewQAction(` + parentName + `)
|
|
|
|
ui.` + a.Name + `.SetObjectName(` + strconv.Quote(a.Name) + `)
|
|
|
|
`)
|
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
// QActions are translated in the parent window's context
|
|
|
|
if prop, ok := propertyByName(a.Properties, "text"); ok {
|
|
|
|
ret.WriteString("ui." + a.Name + `.SetText(` + generateString(prop.StringVal, w.Class) + `)` + "\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
if prop, ok := propertyByName(a.Properties, "shortcut"); ok {
|
|
|
|
ret.WriteString("ui." + a.Name + `.SetShortcut(qt.NewQKeySequence2(` + generateString(prop.StringVal, w.Class) + `))` + "\n")
|
|
|
|
}
|
2024-10-03 05:51:50 +00:00
|
|
|
|
|
|
|
if prop, ok := propertyByName(a.Properties, "icon"); ok {
|
2024-10-03 06:32:28 +00:00
|
|
|
iconName := renderIcon(prop.IconVal, &ret)
|
|
|
|
ret.WriteString(`ui.` + a.Name + `.SetIcon(` + iconName + ")\n")
|
2024-10-03 05:51:50 +00:00
|
|
|
}
|
2024-09-28 02:27:19 +00:00
|
|
|
}
|
2024-09-28 01:04:31 +00:00
|
|
|
|
|
|
|
// Items
|
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
for itemNo, itm := range w.Items {
|
|
|
|
ret.WriteString("ui." + w.Name + `.AddItem("")` + "\n")
|
2024-09-28 01:04:31 +00:00
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
// Check for a "text" property and update the item's text
|
|
|
|
// Do this as a 2nd step so that the SetItemText can be trapped for retranslateUi()
|
|
|
|
// TODO Abstract for all SetItem{Foo} properties
|
2024-10-03 06:31:52 +00:00
|
|
|
for _, prop := range itm.Properties {
|
|
|
|
if prop.Name == "text" {
|
|
|
|
ret.WriteString("ui." + w.Name + `.SetItemText(` + fmt.Sprintf("%d", itemNo) + `, ` + generateString(prop.StringVal, w.Class) + `)` + "\n")
|
|
|
|
} else {
|
|
|
|
ret.WriteString("/* miqt-uic: no handler for item property '" + prop.Name + "' */\n")
|
|
|
|
|
|
|
|
}
|
2024-09-28 02:27:19 +00:00
|
|
|
}
|
|
|
|
}
|
2024-09-28 01:04:31 +00:00
|
|
|
|
|
|
|
// Columns
|
2024-10-03 06:32:05 +00:00
|
|
|
|
|
|
|
for colNo, col := range w.Columns {
|
|
|
|
|
|
|
|
for _, prop := range col.Properties {
|
|
|
|
if prop.Name == "text" {
|
|
|
|
ret.WriteString("ui." + w.Name + ".HeaderItem().SetText(" + fmt.Sprintf("%d", colNo) + ", " + generateString(prop.StringVal, w.Class) + ")\n")
|
|
|
|
} else {
|
|
|
|
ret.WriteString("/* miqt-uic: no handler for column property '" + prop.Name + "' */\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2024-09-28 01:04:31 +00:00
|
|
|
|
|
|
|
// Recurse children
|
2024-09-28 02:27:19 +00:00
|
|
|
var (
|
|
|
|
setCentralWidget = false
|
|
|
|
setMenuBar = false
|
|
|
|
setStatusBar = false
|
|
|
|
)
|
|
|
|
|
2024-10-03 04:09:52 +00:00
|
|
|
for i, child := range w.Widgets {
|
2024-09-28 02:27:19 +00:00
|
|
|
nest, err := generateWidget(child, `ui.`+w.Name, w.Class)
|
2024-09-28 01:04:31 +00:00
|
|
|
if err != nil {
|
2024-10-03 04:09:52 +00:00
|
|
|
return "", fmt.Errorf(w.Name+"/Widgets[%d]: %w", i, err)
|
2024-09-28 01:04:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ret.WriteString(nest)
|
|
|
|
|
|
|
|
// QMainWindow CentralWidget handling
|
|
|
|
// The first listed class can be the central widget.
|
|
|
|
// TODO should it be the first child with a layout? But need to handle windows with no layout
|
2024-09-28 02:27:19 +00:00
|
|
|
if w.Class == `QMainWindow` && !setCentralWidget {
|
2024-09-28 01:04:31 +00:00
|
|
|
ret.WriteString(`ui.` + w.Name + `.SetCentralWidget(ui.` + child.Name + ") // Set central widget \n")
|
2024-09-28 02:27:19 +00:00
|
|
|
setCentralWidget = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// QDockWidget also has something like a central widget
|
|
|
|
if w.Class == `QDockWidget` && !setCentralWidget {
|
|
|
|
ret.WriteString(`ui.` + w.Name + `.SetWidget(ui.` + child.Name + ") // Set central widget \n")
|
|
|
|
setCentralWidget = true
|
|
|
|
}
|
|
|
|
|
2024-10-03 05:51:34 +00:00
|
|
|
if w.Class == "QSplitter" || w.Class == "QStackedWidget" {
|
|
|
|
// We need to manually AddWidget on every child of QSplitter
|
|
|
|
if child.Class == "QWidget" {
|
|
|
|
ret.WriteString(`ui.` + w.Name + `.AddWidget(ui.` + child.Name + `)` + "\n")
|
|
|
|
} else {
|
|
|
|
ret.WriteString(`ui.` + w.Name + `.AddWidget(ui.` + child.Name + `.QWidget)` + "\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
if w.Class == "QMainWindow" && child.Class == "QMenuBar" && !setMenuBar {
|
|
|
|
ret.WriteString(`ui.` + w.Name + `.SetMenuBar(ui.` + child.Name + `)` + "\n")
|
|
|
|
setMenuBar = true
|
|
|
|
}
|
|
|
|
if w.Class == "QMainWindow" && child.Class == "QStatusBar" && !setStatusBar {
|
|
|
|
ret.WriteString(`ui.` + w.Name + `.SetStatusBar(ui.` + child.Name + `)` + "\n")
|
|
|
|
setStatusBar = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// QTabWidget->QTab handling
|
|
|
|
if w.Class == `QTabWidget` {
|
2024-10-03 06:32:28 +00:00
|
|
|
if icon, ok := propertyByName(child.Attributes, "icon"); ok {
|
|
|
|
// AddTab() overload with icon
|
|
|
|
iconName := renderIcon(icon.IconVal, &ret)
|
|
|
|
ret.WriteString(`ui.` + w.Name + `.AddTab2(` + qwidgetName(`ui.`+child.Name, child.Class) + `, ` + iconName + `, "")` + "\n")
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// AddTab() overload without icon
|
|
|
|
ret.WriteString(`ui.` + w.Name + `.AddTab(` + qwidgetName(`ui.`+child.Name, child.Class) + `, "")` + "\n")
|
|
|
|
}
|
2024-09-28 02:27:19 +00:00
|
|
|
}
|
2024-10-05 04:49:21 +00:00
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddActions
|
|
|
|
// n.b. This must be *after* all children have been constructed, in case we
|
|
|
|
// are adding a direct child
|
|
|
|
|
|
|
|
for _, a := range w.AddActions {
|
|
|
|
if a.Name == "separator" {
|
|
|
|
// TODO how does Qt Designer disambiguate a real QAction with name="separator" ?
|
|
|
|
ret.WriteString("ui." + w.Name + ".AddSeparator()\n")
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// If we are a menubar, then <addaction> refers to top-level QMenu instead of QAction
|
|
|
|
if w.Class == "QMenuBar" {
|
|
|
|
ret.WriteString("ui." + w.Name + ".AddMenu(ui." + a.Name + ")\n")
|
2024-10-03 04:23:12 +00:00
|
|
|
} else if w.Class == "QMenu" || w.Class == "QToolBar" {
|
2024-09-28 02:27:19 +00:00
|
|
|
// QMenu has its own .AddAction() implementation that takes plain string
|
|
|
|
// That's convenient, but it shadows the AddAction version that takes a QAction*
|
|
|
|
// We need to use the underlying QWidget.AddAction explicitly
|
|
|
|
ret.WriteString("ui." + w.Name + ".QWidget.AddAction(ui." + a.Name + ")\n")
|
|
|
|
} else {
|
|
|
|
ret.WriteString("ui." + w.Name + ".AddAction(ui." + a.Name + ")\n")
|
|
|
|
}
|
2024-09-28 01:04:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-05 04:49:21 +00:00
|
|
|
if w.Class == "QDialogButtonBox" {
|
|
|
|
// TODO make this using a native connection instead of a C++ -> Go -> C++ roundtrip
|
|
|
|
ret.WriteString(`ui.` + w.Name + `.OnAccepted(` + RootWindowName + ".Accept)\n")
|
|
|
|
ret.WriteString(`ui.` + w.Name + `.OnRejected(` + RootWindowName + ".Reject)\n")
|
|
|
|
}
|
|
|
|
|
2024-09-28 01:04:31 +00:00
|
|
|
return ret.String(), nil
|
2024-09-20 06:39:01 +00:00
|
|
|
}
|
|
|
|
|
2024-09-20 06:51:47 +00:00
|
|
|
func generate(packageName string, goGenerateArgs string, u UiFile) ([]byte, error) {
|
2024-10-03 05:07:43 +00:00
|
|
|
|
2024-09-20 06:39:01 +00:00
|
|
|
ret := strings.Builder{}
|
2024-10-03 05:07:43 +00:00
|
|
|
|
|
|
|
// Update globals for layoutdefault, if present
|
|
|
|
|
|
|
|
if u.LayoutDefault != nil {
|
|
|
|
if u.LayoutDefault.Spacing != nil {
|
|
|
|
DefaultSpacing = *u.LayoutDefault.Spacing
|
|
|
|
}
|
|
|
|
if u.LayoutDefault.Margin != nil {
|
|
|
|
DefaultGridMargin = *u.LayoutDefault.Margin
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Header
|
|
|
|
|
2024-09-20 06:51:47 +00:00
|
|
|
ret.WriteString(`// Generated by miqt-uic. To update this file, edit the .ui file in
|
|
|
|
// Qt Designer, and then run 'go generate'.
|
|
|
|
//
|
|
|
|
//go:` + `generate miqt-uic ` + goGenerateArgs + `
|
|
|
|
|
|
|
|
package ` + packageName + `
|
2024-09-20 06:39:01 +00:00
|
|
|
|
|
|
|
import (
|
2024-09-28 02:27:19 +00:00
|
|
|
"github.com/mappu/miqt/qt"
|
2024-09-20 06:39:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type ` + u.Class + `Ui struct {
|
2024-10-03 04:11:30 +00:00
|
|
|
` + strings.Join(collectClassNames_Widget(&u.Widget), "\n") + `
|
2024-09-20 06:39:01 +00:00
|
|
|
}
|
|
|
|
|
2024-09-28 01:04:31 +00:00
|
|
|
// New` + u.Class + `Ui creates all Qt widget classes for ` + u.Class + `.
|
2024-09-20 06:39:01 +00:00
|
|
|
func New` + u.Class + `Ui() *` + u.Class + `Ui {
|
|
|
|
ui := &` + u.Class + `Ui{}
|
|
|
|
`)
|
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
nest, err := generateWidget(u.Widget, "", "")
|
2024-09-28 01:04:31 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-09-20 06:39:01 +00:00
|
|
|
|
2024-09-28 02:27:19 +00:00
|
|
|
// Don't emit any of the lines that included .Tr(), move them into the
|
|
|
|
// retranslateUi() function
|
|
|
|
var translateFunc []string
|
2024-10-03 06:32:17 +00:00
|
|
|
var setCurrentIndex []string
|
2024-09-28 01:04:31 +00:00
|
|
|
for _, line := range strings.Split(nest, "\n") {
|
2024-09-28 02:27:19 +00:00
|
|
|
if strings.Contains(line, `_Tr(`) {
|
|
|
|
translateFunc = append(translateFunc, line)
|
2024-10-03 06:32:17 +00:00
|
|
|
} else if strings.Contains(line, `.SetCurrentIndex(`) {
|
|
|
|
setCurrentIndex = append(setCurrentIndex, line)
|
2024-09-28 02:27:19 +00:00
|
|
|
} else {
|
2024-09-28 01:04:31 +00:00
|
|
|
ret.WriteString(line + "\n")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-03 06:32:17 +00:00
|
|
|
ret.WriteString("\nui.Retranslate()\n\n")
|
|
|
|
|
|
|
|
for _, sci := range setCurrentIndex {
|
|
|
|
ret.WriteString(sci + "\n")
|
|
|
|
}
|
|
|
|
|
2024-09-28 01:04:31 +00:00
|
|
|
ret.WriteString(`
|
2024-09-28 02:27:19 +00:00
|
|
|
return ui
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retranslate reapplies all text translations.
|
|
|
|
func (ui *` + u.Class + `Ui) Retranslate() {
|
|
|
|
` + strings.Join(translateFunc, "\n") + `
|
2024-09-20 06:39:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
`)
|
|
|
|
|
|
|
|
output := ret.String()
|
2024-09-28 01:04:31 +00:00
|
|
|
|
|
|
|
formatted, err := format.Source([]byte(output))
|
|
|
|
if err != nil {
|
|
|
|
// Return unformatted so it can be fixed
|
|
|
|
return []byte(output), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return formatted, nil
|
2024-09-20 06:39:01 +00:00
|
|
|
}
|