This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch main
in repository ego.
View the commit online.
commit d25428675373550438a06f795a1d345332c2742e
Author: [email protected] <[email protected]>
AuthorDate: Wed Apr 1 19:30:00 2026 -0600
feat(ego-gen): add PartData model and part introspection logic
Add PartData/PartMethodData structs, buildPartsData with collision
detection, and partMethodsForClass for extracting part interfaces.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
cmd/ego-gen/generator.go | 25 +++++++++
cmd/ego-gen/generator_test.go | 67 ++++++++++++++++++++++
cmd/ego-gen/part.go | 128 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 220 insertions(+)
diff --git a/cmd/ego-gen/generator.go b/cmd/ego-gen/generator.go
index a2807f8..de4a538 100644
--- a/cmd/ego-gen/generator.go
+++ b/cmd/ego-gen/generator.go
@@ -42,6 +42,7 @@ type ClassData struct {
GlobalFunctions []GlobalFunctionData // package-level functions with no Eo receiver
CallbackTypes []CallbackTypeData // distinct callback typedefs used by this class
HasCallbacks bool // true if CallbackTypes is non-empty (controls imports/trampolines)
+ Parts []PartData // declared widget parts with accessors
}
// ConstructorData describes a constructor option for an EFL class. Each entry
@@ -238,6 +239,30 @@ type IteratorMethodData struct {
StructCAccessPrefix string
}
+// PartMethodData describes a single method on a part proxy.
+type PartMethodData struct {
+ GoName string // e.g. "Content", "SetContent", "SetText"
+ CHelper string // e.g. "_ego_part_content_get" — C function combining efl_part + interface call
+ IsGetter bool
+ IsSetter bool
+ GoReturnType string // for getters
+ GoParamName string // for setters
+ GoParamType string // for setters
+ IsEoParam bool // true if param is efl.Objecter
+ IsString bool // true if param/return is string
+ IsBool bool // true if return is bool
+ IsColor bool // true if return/param is color.RGBA
+}
+
+// PartData describes a declared part on a widget class.
+type PartData struct {
+ GoAccessorName string // e.g. "Icon" — method on the widget
+ PartName string // e.g. "icon" — Eolian part name
+ ProxyTypeName string // e.g. "defaultItemIconPart" — unexported struct
+ PartClassName string // e.g. "Efl.Ui.Layout_Part_Content"
+ PartMethods []PartMethodData // methods the proxy supports
+}
+
// EventData describes a single event on an EFL class.
type EventData struct {
GoName string
diff --git a/cmd/ego-gen/generator_test.go b/cmd/ego-gen/generator_test.go
index d755dbe..0f1c20f 100644
--- a/cmd/ego-gen/generator_test.go
+++ b/cmd/ego-gen/generator_test.go
@@ -1490,3 +1490,70 @@ func TestGenerateClass_EinaContentParam(t *testing.T) {
}
}
}
+
+func TestGenerateClass_PartAccessor(t *testing.T) {
+ g, outDir := newTestGenerator(t)
+
+ data := ClassData{
+ PackageName: "ui",
+ GoName: "DefaultItem",
+ EolianName: "Efl.Ui.Default_Item",
+ CClassName: "EFL_UI_DEFAULT_ITEM_CLASS",
+ CClassGetFunc: "efl_ui_default_item_class_get",
+ CPrefix: "efl_ui_default_item",
+ EOHeaderFile: "efl_ui_default_item.eo.h",
+ IsAbstract: true,
+ Parts: []PartData{
+ {
+ GoAccessorName: "Icon",
+ PartName: "icon",
+ ProxyTypeName: "defaultItemIconPart",
+ PartMethods: []PartMethodData{
+ {
+ GoName: "Content",
+ CHelper: "_ego_part_content_get",
+ IsGetter: true,
+ GoReturnType: "unsafe.Pointer",
+ },
+ {
+ GoName: "SetContent",
+ CHelper: "_ego_part_content_set",
+ IsSetter: true,
+ GoParamName: "val",
+ GoParamType: "efl.Objecter",
+ IsEoParam: true,
+ },
+ },
+ },
+ },
+ }
+
+ if err := g.GenerateClass(data); err != nil {
+ t.Fatalf("GenerateClass: %v", err)
+ }
+
+ outFile := filepath.Join(outDir, "ui", "default_item.go")
+ content, err := os.ReadFile(outFile)
+ if err != nil {
+ t.Fatalf("not found: %v", err)
+ }
+
+ src := string(content)
+
+ // These will fail until Task 3 adds the template — that's expected.
+ // For now, just verify the file is generated without error.
+ t.Logf("Generated file length: %d bytes", len(content))
+
+ // Check that these are NOT present yet (template hasn't been updated):
+ partsPatterns := []string{
+ "type defaultItemIconPart struct",
+ "func (o *DefaultItem) Icon()",
+ }
+ for _, p := range partsPatterns {
+ if strings.Contains(src, p) {
+ t.Logf("Found part pattern (template already supports parts): %q", p)
+ } else {
+ t.Logf("Part pattern not yet present (expected, template not updated): %q", p)
+ }
+ }
+}
diff --git a/cmd/ego-gen/part.go b/cmd/ego-gen/part.go
new file mode 100644
index 0000000..2b325a0
--- /dev/null
+++ b/cmd/ego-gen/part.go
@@ -0,0 +1,128 @@
+package main
+
+// partMethodsForClass returns the PartMethodData entries for a given part class.
+func partMethodsForClass(partClass *Class) []PartMethodData {
+ var methods []PartMethodData
+
+ for _, f := range partClass.Functions(FunctionProperty) {
+ propName := f.Name()
+ goGetter, goSetter := PropertyToGetterSetter(propName)
+ cGetName := f.FullCName(FunctionPropGet)
+ cSetName := f.FullCName(FunctionPropSet)
+
+ ft := f.Type()
+ hasGet := ft == FunctionProperty || ft == FunctionPropGet
+ hasSet := ft == FunctionProperty || ft == FunctionPropSet
+
+ var goType string
+ var isEo, isString, isBool, isColor bool
+
+ // Determine type from getter return/values.
+ if vals := f.PropertyValues(FunctionPropGet); len(vals) == 1 {
+ pt := vals[0].Type()
+ if pt != nil {
+ cType := pt.CType()
+ goType = CTypeToGo(cType)
+ isEo = pt.IsClass()
+ isString = NeedsStringConversion(cType)
+ isBool = IsBoolType(cType)
+ }
+ } else if rt := f.ReturnType(FunctionPropGet); rt != nil {
+ cType := rt.CType()
+ goType = CTypeToGo(cType)
+ isEo = rt.IsClass()
+ isString = NeedsStringConversion(cType)
+ isBool = IsBoolType(cType)
+ }
+
+ // Check for 4-value color pattern.
+ if vals := f.PropertyValues(FunctionPropGet); len(vals) == 4 {
+ multi, ok := buildMultiValueData(vals)
+ if ok && isColorPattern(multi) {
+ isColor = true
+ goType = "color.RGBA"
+ }
+ }
+
+ if hasGet && goType != "" {
+ methods = append(methods, PartMethodData{
+ GoName: goGetter,
+ CHelper: "_ego_part_" + cGetName,
+ IsGetter: true,
+ GoReturnType: goType,
+ IsString: isString,
+ IsBool: isBool,
+ IsColor: isColor,
+ IsEoParam: isEo,
+ })
+ }
+
+ if hasSet && goType != "" {
+ paramType := goType
+ if isEo {
+ paramType = "efl.Objecter"
+ }
+ if isString {
+ paramType = "string"
+ }
+ if isColor {
+ paramType = "color.RGBA"
+ }
+ methods = append(methods, PartMethodData{
+ GoName: goSetter,
+ CHelper: "_ego_part_" + cSetName,
+ IsSetter: true,
+ GoParamName: "val",
+ GoParamType: paramType,
+ IsEoParam: isEo,
+ IsString: isString,
+ IsBool: isBool,
+ IsColor: isColor,
+ })
+ }
+ }
+
+ return methods
+}
+
+// buildPartsData collects part accessors for a class, with collision detection.
+func buildPartsData(c *Class, goName string, existingNames map[string]bool) []PartData {
+ parts := c.Parts()
+ if len(parts) == 0 {
+ return nil
+ }
+
+ var result []PartData
+ for _, p := range parts {
+ partName := p.Name()
+ accessorName := SnakeToCamel(partName)
+
+ // Collision detection: skip if the widget already has a method/property
+ // with the same Go name.
+ if existingNames[accessorName] {
+ continue
+ }
+
+ partClass := p.Class()
+ if partClass == nil {
+ continue
+ }
+
+ proxyType := lowerFirst(goName) + accessorName + "Part"
+ methods := partMethodsForClass(partClass)
+
+ if len(methods) == 0 {
+ continue
+ }
+
+ result = append(result, PartData{
+ GoAccessorName: accessorName,
+ PartName: partName,
+ ProxyTypeName: proxyType,
+ PartClassName: partClass.Name(),
+ PartMethods: methods,
+ })
+ }
+
+ return result
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.