{"id":24615371,"url":"https://github.com/RemRemRemRe/RemWidgetComponent","last_synced_at":"2025-10-06T07:31:24.636Z","repository":{"id":176118167,"uuid":"462376062","full_name":"RemRemRemRe/RemWidgetComponent","owner":"RemRemRemRe","description":null,"archived":false,"fork":false,"pushed_at":"2024-12-14T15:31:15.000Z","size":65,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2024-12-14T16:30:41.173Z","etag":null,"topics":["gameplay","unreal-engine","widget","widgets"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RemRemRemRe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-22T16:14:03.000Z","updated_at":"2024-12-14T15:31:19.000Z","dependencies_parsed_at":"2024-02-19T16:43:58.501Z","dependency_job_id":"cc1dfe03-c4b4-4a6d-91c5-d74e7103953f","html_url":"https://github.com/RemRemRemRe/RemWidgetComponent","commit_stats":null,"previous_names":["remremremre/remwidgetcomponent"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RemRemRemRe%2FRemWidgetComponent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RemRemRemRe%2FRemWidgetComponent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RemRemRemRe%2FRemWidgetComponent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RemRemRemRe%2FRemWidgetComponent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RemRemRemRe","download_url":"https://codeload.github.com/RemRemRemRe/RemWidgetComponent/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235510119,"owners_count":19001653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gameplay","unreal-engine","widget","widgets"],"created_at":"2025-01-24T22:01:59.307Z","updated_at":"2025-10-06T07:31:24.040Z","avatar_url":"https://github.com/RemRemRemRe.png","language":"C++","funding_links":[],"categories":["UI"],"sub_categories":[],"readme":"# Original intention 💡\nSome logic could be shared between different widget blueprint, like:\n- pull some text from somewhere and show it on this `TextBlock` widget\n\nSo I want to bring `Component` to `UUserWidget`, just like `Actor - Component`\n\nWhich provides an important benefit:\n- layout and logic can be separate completely (all logic can be implemented in components and reused everywhere!)\n\n# Implementation\nThe key problem is how to let component manipulate widgets in owner widget while they are decoupled\n\n## BindWidget\nWe all know that a widget property marked with `BindWidget` or `BindWidgetOptional` will be bind to the widget with same name\n\nBy looking at its implementation (`UWidgetBlueprintGeneratedClass::InitializeWidgetStatic`),\nI found it utilizing the property name to find the widget and set back to the widget property at runtime to accomplish the binding process\n\nSo if we can retrieve the member name, we could do the same process as `BindWidget` to solve this problem\n\n## Data Structure\nBut how could we store the member name? Define a user struct to deal with it? like:\n```C++\nstruct FWidgetBinding\n{\n    UPROPERTY()\n    FName MemberName;\n\n    UPROPERTY()\n    TWeakObjectPtr\u003cUObject\u003e Object;\n};\n```\nThere is a better choice that do the \"two things\" at once : `TSoftObjectPtr`. It provides several benefits:\n- It Stores object full path rather than object pointer, which allows the object address to change for being replaced or re-instanced because of recompiling or reopen the editor without losing the reference\n- When the relative object get renamed, it **should** also \"renamed\" automatically\n- `MemberName` is included in object full path\n- `WeakPtr` is included as a data member\n- Other engine features comes with it\n\nbut with extra overhead:\n- `SubPathString` is a FString\n\nWhich looks acceptable\n\n## Get member name at editor-time\nThe `FWidgetTypeCustomization` that engine provides almost do the job for me, but it has several problems:\n- It only customize the raw widget object pointer ;)\n- It uses the preview widget of blueprint editor as the \"data source\", so the reference is not persistent (reference lost after recompile)\n- It does not customize things in `Instanced Objects`;) (seems like neither of the two types of detail customizations work with `Instanced Objects`)\n\nthe last two problem is \"deadly\" to this, because we need the component to be `instanced`, so I have to write detail customization on my own ;) :\n[WidgetComponentEditor](https://github.com/RemRemRemRe/WidgetComponentEditor)\n\nThe name might be bit of misleading, it only customized the `Components` property (not a hard coded member name) of any given class at the moment, not a brand new editor :)\n\n## Binding at runtime\n@see WidgetComponentStatics::LinkSoftObjectToRuntimeVariable, what it does:\n- build the `WidgetName` to `WidgetObject` map\n- set the object to the `TSoftObjectPtr` with same `Name`\n\nIt should be called when widget is initialized, but this is already handled with a component~ 🍺\n\n## Component ticking\nLuckily, I avoid to reinvent the wheel with the engine provided `UUserWidgetExtension` class, it has all the interface i need,\nand it is feature complete 🎉\n- Initialize    (BeginPlay)\n- Destruct      (EndPlay)\n- RequiresTick  (IsTickEnabled)\n- Tick          (Tick)\n\nDone!\n\n# Example Usage\n## @see UComponentBasedWidget\nBasically, to adopt your custom widget class with this component mechanism:\n1. copy-paste all the code within this class, it does several things for you:\n    - add an `UWidgetComponentAsExtension` component to your widget, it will handle most of the work for you\n        - Dealing with detail customization\n        - `WidgetComponentStatics::LinkSoftObjectToRuntimeVariable`\n    - specify the `Components` property (the instanced components object container)\n    - `WidgetComponentStatics::AddComponentsToWidgetExtension`\n2. add your c++ class of the custom widget to the `UWidgetComponentEditorSetting::WidgetClassToCustomize` through project setting\n3. Done!","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRemRemRemRe%2FRemWidgetComponent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRemRemRemRe%2FRemWidgetComponent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRemRemRemRe%2FRemWidgetComponent/lists"}