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 5d83ff729f355940bf1e81086d234139c699ce38
Author: [email protected] <[email protected]>
AuthorDate: Wed Apr 1 19:43:14 2026 -0600
feat(ego-gen): generate part accessors for all EFL widgets
Widgets with declared parts now generate typed accessor methods.
Panes.First()/Second() and Popup.Backwall() are the active examples.
Fix two generator bugs found during regeneration:
- Deduplicate C helper functions via UniquePartHelpers when multiple
parts share the same part class (e.g. Panes first/second both use
Efl.Ui.Panes_Part, previously causing duplicate static function defs)
- Use the correct C type (e.g. double) instead of void* in generated
helper signatures for numeric part properties like split_ratio_min
- Auto-populate UniquePartHelpers in GenerateClass for backward compat
with tests that construct ClassData directly
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
cmd/ego-gen/generator.go | 9 +++-
cmd/ego-gen/main.go | 1 +
cmd/ego-gen/part.go | 31 +++++++++++++
cmd/ego-gen/templates/class.go.tmpl | 12 +++--
efl/ui/panes.go | 90 +++++++++++++++++++++++++++++++++++++
efl/ui/popup.go | 33 ++++++++++++++
6 files changed, 168 insertions(+), 8 deletions(-)
diff --git a/cmd/ego-gen/generator.go b/cmd/ego-gen/generator.go
index e31360f..4c7b7c0 100644
--- a/cmd/ego-gen/generator.go
+++ b/cmd/ego-gen/generator.go
@@ -42,7 +42,8 @@ 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
+ Parts []PartData // declared widget parts with accessors
+ UniquePartHelpers []PartMethodData // deduplicated C helper entries across all parts (for C preamble)
}
// ConstructorData describes a constructor option for an EFL class. Each entry
@@ -248,6 +249,7 @@ type PartMethodData struct {
GoReturnType string // for getters
GoParamName string // for setters
GoParamType string // for setters
+ CType string // C type for the value param/return (e.g. "double", "int")
IsEoParam bool // true if param is efl.Objecter
IsString bool // true if param/return is string
IsBool bool // true if return is bool
@@ -337,6 +339,11 @@ func NewGenerator(templateDir, outputDir string) (*Generator, error) {
// GenerateClass renders the class template for data and writes the result to
// outputDir/<packagename>/<snake_case_name>.go.
func (g *Generator) GenerateClass(data ClassData) error {
+ // Auto-populate UniquePartHelpers from Parts when the caller has not set it.
+ // This ensures tests that construct ClassData directly still get C helpers emitted.
+ if len(data.UniquePartHelpers) == 0 && len(data.Parts) > 0 {
+ data.UniquePartHelpers = deduplicatePartHelpers(data.Parts)
+ }
return g.generate("class.go.tmpl", data.PackageName, toSnake(data.GoName), data)
}
diff --git a/cmd/ego-gen/main.go b/cmd/ego-gen/main.go
index 3c3cd0d..6741cc9 100644
--- a/cmd/ego-gen/main.go
+++ b/cmd/ego-gen/main.go
@@ -821,6 +821,7 @@ func buildClassData(c *Class, pkgOptionNames map[string]map[string]bool) (ClassD
CallbackTypes: callbackTypes,
HasCallbacks: len(callbackTypes) > 0,
Parts: parts,
+ UniquePartHelpers: deduplicatePartHelpers(parts),
HasColor: hasColorProperty(properties) || hasColorMethod(methods) || hasColorParts(parts),
HasImageTypes: hasImageTypesProperty(properties) || hasImageTypesMethod(methods) || hasImageTypesIndexed(indexedProperties) || hasImageTypesIterator(iteratorMethods) || hasImageTypesConstructors(constructors) || hasImageTypesConstructors(propertyOptions),
HasTime: hasTimeProperty(properties),
diff --git a/cmd/ego-gen/part.go b/cmd/ego-gen/part.go
index 2b325a0..d30ecbf 100644
--- a/cmd/ego-gen/part.go
+++ b/cmd/ego-gen/part.go
@@ -44,12 +44,23 @@ func partMethodsForClass(partClass *Class) []PartMethodData {
}
}
+ // Determine the raw C type for use in generated C helper signatures.
+ var rawCType string
+ if vals := f.PropertyValues(FunctionPropGet); len(vals) == 1 {
+ if pt := vals[0].Type(); pt != nil {
+ rawCType = pt.CType()
+ }
+ } else if rt := f.ReturnType(FunctionPropGet); rt != nil {
+ rawCType = rt.CType()
+ }
+
if hasGet && goType != "" {
methods = append(methods, PartMethodData{
GoName: goGetter,
CHelper: "_ego_part_" + cGetName,
IsGetter: true,
GoReturnType: goType,
+ CType: rawCType,
IsString: isString,
IsBool: isBool,
IsColor: isColor,
@@ -74,6 +85,7 @@ func partMethodsForClass(partClass *Class) []PartMethodData {
IsSetter: true,
GoParamName: "val",
GoParamType: paramType,
+ CType: rawCType,
IsEoParam: isEo,
IsString: isString,
IsBool: isBool,
@@ -126,3 +138,22 @@ func buildPartsData(c *Class, goName string, existingNames map[string]bool) []Pa
return result
}
+
+// deduplicatePartHelpers collects all PartMethodData entries across all parts
+// and returns a deduplicated slice keyed by CHelper name. This is used to emit
+// C helper functions exactly once in the preamble even when multiple parts share
+// the same part class (and therefore have identical helper names).
+func deduplicatePartHelpers(parts []PartData) []PartMethodData {
+ seen := make(map[string]bool)
+ var unique []PartMethodData
+ for _, p := range parts {
+ for _, m := range p.PartMethods {
+ if seen[m.CHelper] {
+ continue
+ }
+ seen[m.CHelper] = true
+ unique = append(unique, m)
+ }
+ }
+ return unique
+}
diff --git a/cmd/ego-gen/templates/class.go.tmpl b/cmd/ego-gen/templates/class.go.tmpl
index 9238186..50bb18b 100644
--- a/cmd/ego-gen/templates/class.go.tmpl
+++ b/cmd/ego-gen/templates/class.go.tmpl
@@ -125,8 +125,7 @@ extern void {{.GoTrampolineName}}(void *data{{range .Params}}, {{.CType}} {{.GoN
{{- end}}
{{- end}}
-{{- range .Parts}}
-{{- range .PartMethods}}
+{{- range .UniquePartHelpers}}
{{- if .IsGetter}}
{{- if .IsString}}
static const char *{{.CHelper}}(const Eo *obj, const char *part_name) {
@@ -145,8 +144,8 @@ static Eo *{{.CHelper}}(const Eo *obj, const char *part_name) {
return {{trimPrefix .CHelper "_ego_part_"}}(efl_part(obj, part_name));
}
{{- else}}
-static void *{{.CHelper}}(const Eo *obj, const char *part_name) {
- return (void *)(uintptr_t){{trimPrefix .CHelper "_ego_part_"}}(efl_part(obj, part_name));
+static {{.CType}} {{.CHelper}}(const Eo *obj, const char *part_name) {
+ return {{trimPrefix .CHelper "_ego_part_"}}(efl_part(obj, part_name));
}
{{- end}}
{{- else if .IsSetter}}
@@ -167,13 +166,12 @@ static void {{.CHelper}}(Eo *obj, const char *part_name, Eo *val) {
{{trimPrefix .CHelper "_ego_part_"}}(efl_part(obj, part_name), val);
}
{{- else}}
-static void {{.CHelper}}(Eo *obj, const char *part_name, void *val) {
+static void {{.CHelper}}(Eo *obj, const char *part_name, {{.CType}} val) {
{{trimPrefix .CHelper "_ego_part_"}}(efl_part(obj, part_name), val);
}
{{- end}}
{{- end}}
{{- end}}
-{{- end}}
{{- range .Properties}}
{{- if and .HasGet (isPointerType .GoType)}}
@@ -1280,7 +1278,7 @@ func (p {{$part.ProxyTypeName}}) {{$m.GoName}}({{$m.GoParamName}} efl.Objecter)
func (p {{$part.ProxyTypeName}}) {{$m.GoName}}({{$m.GoParamName}} {{$m.GoParamType}}) {
cName := C.CString(p.name)
defer C.free(unsafe.Pointer(cName))
- C.{{$m.CHelper}}((*C.Eo)(p.parent), cName, {{$m.GoParamName}})
+ C.{{$m.CHelper}}((*C.Eo)(p.parent), cName, C.{{cTypeToCgo $m.CType}}({{$m.GoParamName}}))
}
{{- end}}
{{- end}}
diff --git a/efl/ui/panes.go b/efl/ui/panes.go
index 71609ec..c2afb2f 100644
--- a/efl/ui/panes.go
+++ b/efl/ui/panes.go
@@ -97,6 +97,18 @@ typedef Eo Efl_Ui_Widget;
// Declare the class_get function directly rather than including the full .eo.h
// header, which may reference types from other .eo files that are not included.
extern const Efl_Class *efl_ui_panes_class_get(void);
+static Eina_Bool _ego_part_efl_ui_panes_part_hint_min_allow_get(const Eo *obj, const char *part_name) {
+ return efl_ui_panes_part_hint_min_allow_get(efl_part(obj, part_name));
+}
+static void _ego_part_efl_ui_panes_part_hint_min_allow_set(Eo *obj, const char *part_name, Eina_Bool val) {
+ efl_ui_panes_part_hint_min_allow_set(efl_part(obj, part_name), val);
+}
+static double _ego_part_efl_ui_panes_part_split_ratio_min_get(const Eo *obj, const char *part_name) {
+ return efl_ui_panes_part_split_ratio_min_get(efl_part(obj, part_name));
+}
+static void _ego_part_efl_ui_panes_part_split_ratio_min_set(Eo *obj, const char *part_name, double val) {
+ efl_ui_panes_part_split_ratio_min_set(efl_part(obj, part_name), val);
+}
// _ego_get_efl_ui_layout_orientation_get wraps the getter, casting the return value through
// uintptr_t so that both pointer and integer returns become void *.
// This prevents C type conflicts when Eolian's type differs from the header.
@@ -290,3 +302,81 @@ func (o *Panes) OnLongpressed(fn func(value int)) efl.Handle {
fn(int(*(*C.int)(info)))
})
}
+
+// panesFirstPart provides access to the "first" part of Panes.
+type panesFirstPart struct {
+ parent unsafe.Pointer
+ name string
+}
+
+// First returns the first part of Panes.
+func (o *Panes) First() panesFirstPart {
+ return panesFirstPart{parent: o.Eo(), name: "first"}
+}
+
+func (p panesFirstPart) HintMinAllow() bool {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ return C._ego_part_efl_ui_panes_part_hint_min_allow_get((*C.Eo)(p.parent), cName) != 0
+}
+
+func (p panesFirstPart) SetHintMinAllow(val bool) {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ var cVal C.Eina_Bool
+ if val {
+ cVal = 1
+ }
+ C._ego_part_efl_ui_panes_part_hint_min_allow_set((*C.Eo)(p.parent), cName, cVal)
+}
+
+func (p panesFirstPart) SplitRatioMin() float64 {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ return float64(C._ego_part_efl_ui_panes_part_split_ratio_min_get((*C.Eo)(p.parent), cName))
+}
+
+func (p panesFirstPart) SetSplitRatioMin(val float64) {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ C._ego_part_efl_ui_panes_part_split_ratio_min_set((*C.Eo)(p.parent), cName, C.double(val))
+}
+
+// panesSecondPart provides access to the "second" part of Panes.
+type panesSecondPart struct {
+ parent unsafe.Pointer
+ name string
+}
+
+// Second returns the second part of Panes.
+func (o *Panes) Second() panesSecondPart {
+ return panesSecondPart{parent: o.Eo(), name: "second"}
+}
+
+func (p panesSecondPart) HintMinAllow() bool {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ return C._ego_part_efl_ui_panes_part_hint_min_allow_get((*C.Eo)(p.parent), cName) != 0
+}
+
+func (p panesSecondPart) SetHintMinAllow(val bool) {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ var cVal C.Eina_Bool
+ if val {
+ cVal = 1
+ }
+ C._ego_part_efl_ui_panes_part_hint_min_allow_set((*C.Eo)(p.parent), cName, cVal)
+}
+
+func (p panesSecondPart) SplitRatioMin() float64 {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ return float64(C._ego_part_efl_ui_panes_part_split_ratio_min_get((*C.Eo)(p.parent), cName))
+}
+
+func (p panesSecondPart) SetSplitRatioMin(val float64) {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ C._ego_part_efl_ui_panes_part_split_ratio_min_set((*C.Eo)(p.parent), cName, C.double(val))
+}
diff --git a/efl/ui/popup.go b/efl/ui/popup.go
index fc64a6e..85cc80d 100644
--- a/efl/ui/popup.go
+++ b/efl/ui/popup.go
@@ -106,6 +106,12 @@ typedef Eo Efl_Ui_Widget;
// Declare the class_get function directly rather than including the full .eo.h
// header, which may reference types from other .eo files that are not included.
extern const Efl_Class *efl_ui_popup_class_get(void);
+static Eina_Bool _ego_part_efl_ui_popup_part_backwall_repeat_events_get(const Eo *obj, const char *part_name) {
+ return efl_ui_popup_part_backwall_repeat_events_get(efl_part(obj, part_name));
+}
+static void _ego_part_efl_ui_popup_part_backwall_repeat_events_set(Eo *obj, const char *part_name, Eina_Bool val) {
+ efl_ui_popup_part_backwall_repeat_events_set(efl_part(obj, part_name), val);
+}
// _ego_get_efl_ui_popup_align_get wraps the getter, casting the return value through
// uintptr_t so that both pointer and integer returns become void *.
// This prevents C type conflicts when Eolian's type differs from the header.
@@ -595,3 +601,30 @@ func (o *Popup) OnDirtyLogicFreezeChanged(fn func(value bool)) efl.Handle {
fn(*(*C.Eina_Bool)(info) != 0)
})
}
+
+// popupBackwallPart provides access to the "backwall" part of Popup.
+type popupBackwallPart struct {
+ parent unsafe.Pointer
+ name string
+}
+
+// Backwall returns the backwall part of Popup.
+func (o *Popup) Backwall() popupBackwallPart {
+ return popupBackwallPart{parent: o.Eo(), name: "backwall"}
+}
+
+func (p popupBackwallPart) RepeatEvents() bool {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ return C._ego_part_efl_ui_popup_part_backwall_repeat_events_get((*C.Eo)(p.parent), cName) != 0
+}
+
+func (p popupBackwallPart) SetRepeatEvents(val bool) {
+ cName := C.CString(p.name)
+ defer C.free(unsafe.Pointer(cName))
+ var cVal C.Eina_Bool
+ if val {
+ cVal = 1
+ }
+ C._ego_part_efl_ui_popup_part_backwall_repeat_events_set((*C.Eo)(p.parent), cName, cVal)
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.