├── .gitignore ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── README_RU.md ├── README_RU.md.meta ├── Scripts.meta ├── Scripts ├── Editor.meta ├── Editor │ ├── Laphed.UIFramework.UnityBridge.Editor.asmdef │ ├── Laphed.UIFramework.UnityBridge.Editor.asmdef.meta │ ├── WindowCustomInspector.cs │ └── WindowCustomInspector.cs.meta ├── Runtime.meta └── Runtime │ ├── MVP.meta │ ├── MVP │ ├── CollectionPresenter.cs │ ├── CollectionPresenter.cs.meta │ ├── DictionaryPresenter.cs │ ├── DictionaryPresenter.cs.meta │ ├── IPresenter.cs │ ├── IPresenter.cs.meta │ ├── IView.cs │ ├── IView.cs.meta │ ├── Laphed.MVP.asmdef │ ├── Laphed.MVP.asmdef.meta │ ├── MonoBehaviourViews.meta │ ├── MonoBehaviourViews │ │ ├── MonoSpriteView.cs │ │ ├── MonoSpriteView.cs.meta │ │ ├── MonoTextView.cs │ │ ├── MonoTextView.cs.meta │ │ ├── MonoView.cs │ │ └── MonoView.cs.meta │ ├── PresenterBase.cs │ ├── PresenterBase.cs.meta │ ├── PropertyPresenter.cs │ ├── PropertyPresenter.cs.meta │ ├── ReactivePresenterBase.cs │ └── ReactivePresenterBase.cs.meta │ ├── UIFramework.meta │ └── UIFramework │ ├── IUiBinder.cs │ ├── IUiBinder.cs.meta │ ├── IWindow.cs │ ├── IWindow.cs.meta │ ├── Laphed.UIFramework.asmdef │ ├── Laphed.UIFramework.asmdef.meta │ ├── UiBinder.cs │ ├── UiBinder.cs.meta │ ├── UnityMonoBridge.meta │ ├── UnityMonoBridge │ ├── MonoBinder.cs │ ├── MonoBinder.cs.meta │ ├── MonoWindow.cs │ └── MonoWindow.cs.meta │ ├── Window.cs │ └── Window.cs.meta ├── package.json └── package.json.meta /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /ios/ 7 | /[Tt]emp/ 8 | /[Oo]bj/ 9 | /[Bb]uild/ 10 | /[Bb]uilds/ 11 | /[Ll]ogs/ 12 | /[Uu]ser[Ss]ettings/ 13 | /[Rr]ecordings 14 | 15 | # MemoryCaptures can get excessive in size. 16 | # They also could contain extremely sensitive data 17 | /[Mm]emoryCaptures/ 18 | 19 | # Asset meta data should only be ignored when the corresponding asset is also ignored 20 | !/[Aa]ssets/**/*.meta 21 | 22 | # Uncomment this line if you wish to ignore the asset store tools plugin 23 | # /[Aa]ssets/AssetStoreTools* 24 | 25 | # Autogenerated Jetbrains Rider plugin 26 | /[Aa]ssets/Plugins/Editor/JetBrains* 27 | 28 | # Visual Studio cache directory 29 | .vs/ 30 | .idea 31 | .git 32 | 33 | # Gradle cache directory 34 | .gradle/ 35 | 36 | # Autogenerated VS/MD/Consulo solution and project files 37 | ExportedObj/ 38 | .consulo/ 39 | *.csproj 40 | *.unityproj 41 | *.sln 42 | *.suo 43 | *.tmp 44 | *.user 45 | *.userprefs 46 | *.pidb 47 | *.booproj 48 | *.svd 49 | *.pdb 50 | *.mdb 51 | *.opendb 52 | *.VC.db 53 | 54 | # Unity3D generated meta files 55 | *.pidb.meta 56 | *.pdb.meta 57 | *.mdb.meta 58 | 59 | # Unity3D generated file on crash reports 60 | sysinfo.txt 61 | 62 | # Builds 63 | *.apk 64 | *.unitypackage 65 | /[Aa]ssets/[Ss]treaming[Aa]ssets/build_inf* 66 | 67 | # Crashlytics generated file 68 | crashlytics-build.properties 69 | 70 | # Packed Addressables 71 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* 72 | 73 | # Temporary auto-generated Android Assets 74 | /[Aa]ssets/[Ss]treamingAssets/aa.meta 75 | /[Aa]ssets/[Ss]treamingAssets/aa/* 76 | 77 | # Exceptions 78 | !*.dll 79 | !*.obj 80 | 81 | #Other files 82 | /[Aa]ssets/[Ss]cenes/currentScenePath* 83 | 84 | #Mac metafiles 85 | ._* 86 | Assets/Plugins/Zenject/Source/Zenject.csproj.meta 87 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Alexander Gavrilushkin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9e1a3d49c37f6914da7931c168e872a2 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UI Framework 2 | [Документация на русском языке](https://github.com/laphedhendad/UI-Framework/blob/main/README_RU.md) 3 | 4 | 5 | UI Framework for Unity based on reactivity and Model-View-Presenter pattern. 6 | 7 | ## Table of Contents 8 | 9 | - [Installation](#installation) 10 | - [Reactivity](#reactivity) 11 | - [Model-View-Presenter](#model-view-presenter) 12 | - [Model](#model) 13 | - [View](#view) 14 | - [Presenter](#presenter) 15 | - [Simple Presenter](#simple-presenter) 16 | - [UI Architecture](#ui-architecture) 17 | - [Example Project](#example-project) 18 | 19 | # Installation 20 | 21 | $${\color{orange}IMPORTANT!}$$ 22 | 23 | The framework depends on the custom [lightweight reactivity package](https://github.com/laphedhendad/Lightweight-Reactivity). You need to install this before installing the UI Framework. 24 |
25 | 26 | * Via .unitypackage file: 27 | 28 | Download package from [releases page](https://github.com/laphedhendad/UI-Framework/releases). 29 | Add to project with Assets/Import Package/Custom Package... 30 | 31 | * Via git URL: 32 | 33 | Open Window/Package Manager and choose +/Add package from git URL... 34 | 35 | Set `https://github.com/laphedhendad/UI-Framework.git` as URL. 36 | 37 | If you want to set a target version, UI Framework uses the \*.\*.\* release tag so you can specify a version like #1.0.0. For example `https://github.com/laphedhendad/UI-Framework.git#1.0.0`. 38 | 39 | # Reactivity 40 | 41 | The interaction between interface and data is built on a reactive approach. The package implements reactive property, collection and dictionary. They are lightweight versions of the corresponding classes from the [UniRx](https://github.com/neuecc/UniRx) plugin. 42 | 43 | ## ReactiveProperty 44 | 45 | Reactive Property - a simple wrapper around data types. Triggers the OnChanged event when its value changes. 46 | 47 | ```csharp 48 | public class ReactiveProperty: IReactiveProperty 49 | { 50 | public event Action OnChanged; 51 | private T currentValue; 52 | 53 | public T Value 54 | { 55 | get => currentValue; 56 | set 57 | { 58 | if (EqualityComparer.Default.Equals(value, currentValue)) return; 59 | currentValue = value; 60 | OnChanged?.Invoke(); 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | ## ReactiveCollection 67 | 68 | Reactive Collection - a wrapper around a type [Collection](https://learn.microsoft.com/ru-ru/dotnet/api/system.collections.objectmodel.collection-1). Triggers events: 69 | 70 | * OnChanged 71 | * OnAdd 72 | * OnRemove 73 | * OnReplace 74 | * OnCleared 75 | 76 | The events pass the index of the changed element as parameters. 77 | 78 | ## ReactiveDictionary 79 | 80 | Reactive Dictionary - a wrapper around a type [Dictionary](https://learn.microsoft.com/ru-ru/dotnet/api/system.collections.generic.dictionary-2). Triggers events: 81 | 82 | * OnChanged 83 | * OnAdd 84 | * OnRemove 85 | * OnReplace 86 | * OnCleared 87 | 88 | The events pass the key of the changed element as parameters. 89 | 90 | ## Example 91 | 92 | ```csharp 93 | //booster class with reactive property 94 | public class Booster: IBooster 95 | { 96 | public IReactiveProperty Amount { get; } = new ReactiveProperty(); 97 | } 98 | 99 | ... 100 | 101 | //subscribing to a reactive property 102 | public override void SubscribeModel(TReactive model) 103 | { 104 | if (model == null) return; 105 | this.model = model; 106 | model.OnChanged += HandleModelUpdate; 107 | } 108 | 109 | ... 110 | 111 | //changing value of a reactive property 112 | private void BuyBooster() 113 | { 114 | booster.Amount.Value++; 115 | } 116 | ``` 117 | 118 | # Model-View-Presenter 119 | 120 | The MVP pattern involves breaking the connection between Model and View with the help of an additional Presenter class. 121 | 122 | Presenter: 123 | * Subscribes to model events and can directly modify data 124 | * Holds a reference to View and updates it through the interface 125 | * Subscribes to View events and processes them 126 | * Has the same lifetime as View and is associated with only one view 127 | 128 | ![MVP](https://github.com/laphedhendad/UI-Framework/assets/52206303/bae3b5fe-b01c-496d-9ecf-6b93770fee03) 129 | 130 | ## Model 131 | 132 | Reactive property/collection/dictionary acts as the model. View implements the IView interface: 133 | 134 | ```csharp 135 | public interface IView 136 | { 137 | event Action OnDispose; 138 | void UpdateView(T value); 139 | } 140 | ``` 141 | 142 | ## View 143 | 144 | For MonoBehaviour views in the package, there is an abstract class MonoView and its descendants for specific data types: 145 | 146 | ```csharp 147 | //base class for MonoBehaviour Views 148 | public abstract class MonoView: MonoBehaviour, IView 149 | { 150 | public event Action OnDispose; 151 | public abstract void UpdateView(T value); 152 | protected virtual void OnDestroy() => OnDispose?.Invoke(); 153 | } 154 | 155 | //MonoView for concrete data type 156 | public abstract class MonoTextView: MonoView 157 | { 158 | protected abstract string Text { set; } 159 | public override void UpdateView(string value) => Text = value; 160 | } 161 | 162 | //concrete View realization 163 | public class SpeakerNameView : MonoTextView 164 | { 165 | [SerializeField] private TMP_Text speakerNameText; 166 | protected override string Text 167 | { 168 | set => speakerNameText.text = value; 169 | } 170 | } 171 | ``` 172 | 173 | You can create MonoView for your own data types. Existing solutions in the package cover only the most common types. 174 | 175 | ## Presenter 176 | 177 | Presenter implements the IPresenter interface: 178 | 179 | ```csharp 180 | public interface IPresenter: IDisposable 181 | { 182 | void SubscribeModel(TModel model); 183 | } 184 | ``` 185 | 186 | Interaction with different reactive models involves using the corresponding presenter. The package includes: 187 | 188 | * PropertryPresenter for properties 189 | * CollectionPresenter for collections 190 | * DictionaryPresenter for dictionaries 191 | 192 | For each of the above classes, there is a paired class for situations where type conversion is not required for display. 193 | 194 | * PropertyPresenter 195 | * CollectionPresenter 196 | * DictionaryPresenter 197 | 198 | Creating your own Presenter involves inheriting from these six classes and overriding the SubscribeModel and HandleModelUpdate methods if necessary. 199 | 200 | ```csharp 201 | //example presenter for displaying the number of boosters and 202 | //handling the booster button click 203 | public class BoosterPresenter: PropertyPresenter 204 | { 205 | //specify data type of the View to 206 | //handle user input events 207 | private new readonly BoosterButton view; 208 | 209 | private readonly ModalWindowView buyBoosterWindow; 210 | 211 | public BoosterPresenter(BoosterButton view, ModalWindowView buyBoosterWindow) : base(view) 212 | { 213 | this.view = view; 214 | this.buyBoosterWindow = buyBoosterWindow; 215 | this.view.OnClicked += HandleClick; 216 | } 217 | 218 | private void HandleClick() 219 | { 220 | if (model == null) return; 221 | if (model.Value == 0) 222 | { 223 | buyBoosterWindow.Open(); 224 | return; 225 | } 226 | model.Value--; 227 | } 228 | 229 | public override void Dispose() 230 | { 231 | view.OnClicked -= HandleClick; 232 | base.Dispose(); 233 | } 234 | } 235 | 236 | ... 237 | 238 | //subscribing Presenter to Model 239 | public override void Bind() 240 | { 241 | boosterPresenter = new BoosterPresenter(boosterButton, buyBoosterWindow); 242 | boosterPresenter.SubscribeModel(booster.Amount); 243 | } 244 | ``` 245 | 246 | ## Simple Presenter 247 | 248 | There are situations where the presentation only needs to display the model's value without extra logic. In such cases, instances of PropertyPresenter, CollectionPresenter, DictionaryPresenter are created. 249 | 250 | ```csharp 251 | public void Bind() 252 | { 253 | //creating of PropertyPresenter instance 254 | var speakerNamePresenter = new PropertyPresenter(speakerNameView); 255 | 256 | //subscribing Presenter to Model 257 | speakerNamePresenter.SubscribeModel(reactivePassage.SpeakerName); 258 | } 259 | ``` 260 | 261 | # UI Architecture 262 | 263 | The entire UI in the package is divided into Windows and elements. A Window is a container for elements, including nested windows. A Window can refer to either the entire application screen or a separate modal window. Windows have only two responsibilities: determining the order of binder initialization (see below) and invoking resource cleanup upon destruction. 264 | 265 | The UI is connected to the rest of the application code through binders. References to components are passed to the binder, and their interaction with child UI elements is described. Each window should have its corresponding binder, and a binder can reference elements from windows located hierarchically at any position relative to its window. 266 | 267 | Creating a window involves creating a GameObject with the MonoWindow component and a specific implementation of MonoBinder. 268 | 269 | ![MonoWindow](https://github.com/laphedhendad/UI-Framework/assets/52206303/2e084ed9-16e5-4c00-97b6-ce4eb7d77449) 270 | 271 | References to its child windows are passed to the MonoWindow component. Doing this manually is not mandatory. It is sufficient to press the "Find subwindows automatically" button on the root MonoWindow component before testing the application. All windows in the hierarchy will automatically set references to their child windows. 272 | 273 | ```csharp 274 | //example of Binder realization for booster activating window 275 | public class BoostersBinder : MonoBinder 276 | { 277 | [SerializeField] private BoosterButton boosterButton; 278 | [SerializeField] private ModalWindowView buyBoosterWindow; 279 | 280 | private IBooster booster; 281 | private BoosterPresenter boosterPresenter; 282 | 283 | [Inject] 284 | private void Construct(IBooster booster) 285 | { 286 | this.booster = booster; 287 | } 288 | 289 | public override void Bind() 290 | { 291 | boosterPresenter = new BoosterPresenter(boosterButton, buyBoosterWindow); 292 | boosterPresenter.SubscribeModel(booster.Amount); 293 | } 294 | 295 | protected override void Unbind() 296 | { 297 | boosterPresenter?.Dispose(); 298 | } 299 | } 300 | ``` 301 | 302 | # Example Project 303 | 304 | [The example project](https://github.com/laphedhendad/UI-Framework-Example) can be downloaded as an archive from GitHub. The project uses [Zenject](https://github.com/modesttree/Zenject) and [UniTask](https://github.com/Cysharp/UniTask) in a relatively small amount to not distract attention from the package. 305 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f61b6ad80009cdf4b9f166f826227b2d 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README_RU.md: -------------------------------------------------------------------------------- 1 | # UI Framework 2 | 3 | Фреймворк для создания гибкого интерфейса на движке Unity, основанный на реактивности и паттерне Model-View-Presenter. 4 | 5 | ## Содержание 6 | 7 | - [Установка](#установка) 8 | - [Реактивность](#реактивность) 9 | - [Model-View-Presenter](#model-view-presenter) 10 | - [Model](#model) 11 | - [View](#view) 12 | - [Presenter](#presenter) 13 | - [Простой Presenter](#простой-presenter) 14 | - [Архитектура UI](#архитектура-ui) 15 | - [Обучающий проект](#обучающий-проект) 16 | 17 | # Установка 18 | 19 | $${\color{orange}ВАЖНО!}$$ 20 | 21 | Фреймворк использует в качестве зависимости другой пакет [Lightweight Reactivity Package](https://github.com/laphedhendad/Lightweight-Reactivity). Установите его перед установкой UI Framework. 22 |
23 | 24 | * Через .unitypackage файл: 25 | 26 | Скачайте пакет со [страницы релизов](https://github.com/laphedhendad/UI-Framework/releases). 27 | Добавьте его в свой проект с помощью Assets/Import Package/Custom Package... 28 | * Через ссылку на git: 29 | 30 | Откройте Window/Package Manager, выберите +/Add package from git URL... 31 | 32 | Укажите ссылку на пакет: `https://github.com/laphedhendad/UI-Framework.git` 33 | 34 | Можно выбрать конкретную версию пакета, указав её в ссылке: `https://github.com/laphedhendad/UI-Framework.git#1.0.0` 35 | 36 | # Реактивность 37 | 38 | Взаимодействие интерфейса и данных построено на реактивном подходе. В пакете реализовано реактивное свойство, коллекция и словарь. Они представляют собой облегченные версии соответствующих классов из плагина [UniRx](https://github.com/neuecc/UniRx). 39 | 40 | ## ReactiveProperty 41 | 42 | Реактивное свойство - простая обертка над типами данных. Вызывает событие OnChanged, когда меняется его значение. 43 | 44 | ```csharp 45 | public class ReactiveProperty: IReactiveProperty 46 | { 47 | public event Action OnChanged; 48 | private T currentValue; 49 | 50 | public T Value 51 | { 52 | get => currentValue; 53 | set 54 | { 55 | if (EqualityComparer.Default.Equals(value, currentValue)) return; 56 | currentValue = value; 57 | OnChanged?.Invoke(); 58 | } 59 | } 60 | } 61 | ``` 62 | 63 | ## ReactiveCollection 64 | 65 | Реактивная коллекция - обертка над типом [Collection](https://learn.microsoft.com/ru-ru/dotnet/api/system.collections.objectmodel.collection-1). Вызывает события: 66 | 67 | * OnChanged 68 | * OnAdd 69 | * OnRemove 70 | * OnReplace 71 | * OnCleared 72 | 73 | В параметрах событий передает индекс элемента, подвергшегося изменениям. 74 | 75 | ## ReactiveDictionary 76 | 77 | Реактивный словарь - обертка над типом [Dictionary](https://learn.microsoft.com/ru-ru/dotnet/api/system.collections.generic.dictionary-2). Вызывает события: 78 | 79 | * OnChanged 80 | * OnAdd 81 | * OnRemove 82 | * OnReplace 83 | * OnCleared 84 | 85 | В параметрах событий передает ключ элемента, подвергшегося изменениям. 86 | 87 | ## Пример использования 88 | 89 | ```csharp 90 | //класс бустера с реактивным свойством 91 | public class Booster: IBooster 92 | { 93 | public IReactiveProperty Amount { get; } = new ReactiveProperty(); 94 | } 95 | 96 | ... 97 | 98 | //подписка на реактивное свойство 99 | public override void SubscribeModel(TReactive model) 100 | { 101 | if (model == null) return; 102 | this.model = model; 103 | model.OnChanged += HandleModelUpdate; 104 | } 105 | 106 | ... 107 | 108 | //изменение значения реактивного свойства 109 | private void BuyBooster() 110 | { 111 | booster.Amount.Value++; 112 | } 113 | ``` 114 | 115 | # Model-View-Presenter 116 | 117 | Паттерн MVP предполагает разбитие связи между Model(модель данных) и View(отображение) с помощью дополнительного класса Presenter(представитель). 118 | 119 | Presenter: 120 | * Подписывается на события модели и может напрямую менять данные 121 | * Хранит ссылку на View и обновляет его через интерфейс 122 | * Подписывается на события View и обрабатывает их 123 | * Имеет одинаковое с View время жизни и связан только с одним отображением 124 | 125 | ![MVP](https://github.com/laphedhendad/UI-Framework/assets/52206303/bae3b5fe-b01c-496d-9ecf-6b93770fee03) 126 | 127 | ## Model 128 | 129 | В роли модели выступает реактивное свойство/коллекция/словарь. View реализует интерфейс IView: 130 | 131 | ```csharp 132 | public interface IView 133 | { 134 | event Action OnDispose; 135 | void UpdateView(T value); 136 | } 137 | ``` 138 | 139 | ## View 140 | 141 | Для MonoBehaviour отображений в пакете присутствует абстрактный класс MonoView и его наследники для конкретных типов данных: 142 | 143 | ```csharp 144 | //базовый класс для MonoBehaviour отображений 145 | public abstract class MonoView: MonoBehaviour, IView 146 | { 147 | public event Action OnDispose; 148 | public abstract void UpdateView(T value); 149 | protected virtual void OnDestroy() => OnDispose?.Invoke(); 150 | } 151 | 152 | //MonoView для конкретного типа данных 153 | public abstract class MonoTextView: MonoView 154 | { 155 | protected abstract string Text { set; } 156 | public override void UpdateView(string value) => Text = value; 157 | } 158 | 159 | //конкретная реализация View 160 | public class SpeakerNameView : MonoTextView 161 | { 162 | [SerializeField] private TMP_Text speakerNameText; 163 | protected override string Text 164 | { 165 | set => speakerNameText.text = value; 166 | } 167 | } 168 | ``` 169 | 170 | Вы можете сами создавать MonoView для собственных типов данных. Существующие в пакете решения покрывают только самые частые типы. 171 | 172 | ## Presenter 173 | 174 | Presenter реализует интерфейс IPresenter: 175 | 176 | ```csharp 177 | public interface IPresenter: IDisposable 178 | { 179 | void SubscribeModel(TModel model); 180 | } 181 | ``` 182 | 183 | Взаимодействие с разными реактивными моделями предполагает использование соответствующего представителя. В пакете представлены: 184 | 185 | * PropertryPresenter для свойств 186 | * CollectionPresenter для коллекций 187 | * DictionaryPresenter для словарей 188 | 189 | Для каждого из вышеперечисленных классов существует парный класс для ситуаций, в которых не требуется конвертация типов данных для отображения. 190 | 191 | * PropertyPresenter 192 | * CollectionPresenter 193 | * DictionaryPresenter 194 | 195 | Создание собственного Presenter'а предполагает наследование от этих 6 классов и переопределение методов SubscribeModel и HandleModelUpdate при необходимости. 196 | 197 | ```csharp 198 | //пример представителя для отображения количества бустеров 199 | //и обработки нажатия на кнопку бустера 200 | public class BoosterPresenter: PropertyPresenter 201 | { 202 | //конкретизируем тип данных отображения 203 | //для обработки событий пользовательского ввода 204 | private new readonly BoosterButton view; 205 | 206 | private readonly ModalWindowView buyBoosterWindow; 207 | 208 | public BoosterPresenter(BoosterButton view, ModalWindowView buyBoosterWindow) : base(view) 209 | { 210 | this.view = view; 211 | this.buyBoosterWindow = buyBoosterWindow; 212 | this.view.OnClicked += HandleClick; 213 | } 214 | 215 | private void HandleClick() 216 | { 217 | if (model == null) return; 218 | if (model.Value == 0) 219 | { 220 | buyBoosterWindow.Open(); 221 | return; 222 | } 223 | model.Value--; 224 | } 225 | 226 | public override void Dispose() 227 | { 228 | view.OnClicked -= HandleClick; 229 | base.Dispose(); 230 | } 231 | } 232 | 233 | ... 234 | 235 | //подписка презентера на модель 236 | public override void Bind() 237 | { 238 | boosterPresenter = new BoosterPresenter(boosterButton, buyBoosterWindow); 239 | boosterPresenter.SubscribeModel(booster.Amount); 240 | } 241 | ``` 242 | 243 | ## Простой Presenter 244 | 245 | Часто возникают ситуации, когда отображению необходимо только показывать значение модели без лишней логики. В таких кейсах создаются экземпляры классов PropertyPresenter, CollectionPresenter, DictionaryPresenter. 246 | 247 | ```csharp 248 | public void Bind() 249 | { 250 | //создание PropertyPresenter без наследования 251 | var speakerNamePresenter = new PropertyPresenter(speakerNameView); 252 | 253 | //подписка представителя на модель 254 | speakerNamePresenter.SubscribeModel(reactivePassage.SpeakerName); 255 | } 256 | ``` 257 | 258 | # Архитектура UI 259 | 260 | Весь UI в пакете делится на Окна (Window) и элементы. Окно - это контейнер для элементов (включая вложенные окна). Окном может называться как весь экран приложения, так и отдельное модальное окно. У окон только две ответственности: определять порядок инициализации binder'ов (см. ниже) и вызывать очистку ресурсов при уничтожении. 261 | 262 | UI связывается с остальным кодом приложения через binder. В binder прокидываются ссылки на компоненты и описывается их взаимодействие с дочерними ему элементами UI. Каждому окну должен соответствовать свой binder, при этом binder может ссылаться на элементы из окон, находящихся в иерархии на любой позиции относительно его окна. 263 | 264 | Создание окна предполагает создание GameObject'а с компонентом MonoWindow и конкретной реализацией MonoBinder. 265 | 266 | ![MonoWindow](https://github.com/laphedhendad/UI-Framework/assets/52206303/2e084ed9-16e5-4c00-97b6-ce4eb7d77449) 267 | 268 | В компонент MonoWindow прокидываются ссылки на его дочерние окна. Делать это руками не обязательно. Достаточно перед тестом приложения нажать кнопку "Find subwindows automatically" на корневом компоненте MonoWindow. Все окна в иерархии сами установят ссылки на дочерние окна. 269 | 270 | ```csharp 271 | //пример реализации Binder'а для окна использования бустера 272 | public class BoostersBinder : MonoBinder 273 | { 274 | [SerializeField] private BoosterButton boosterButton; 275 | [SerializeField] private ModalWindowView buyBoosterWindow; 276 | 277 | private IBooster booster; 278 | private BoosterPresenter boosterPresenter; 279 | 280 | [Inject] 281 | private void Construct(IBooster booster) 282 | { 283 | this.booster = booster; 284 | } 285 | 286 | public override void Bind() 287 | { 288 | boosterPresenter = new BoosterPresenter(boosterButton, buyBoosterWindow); 289 | boosterPresenter.SubscribeModel(booster.Amount); 290 | } 291 | 292 | protected override void Unbind() 293 | { 294 | boosterPresenter?.Dispose(); 295 | } 296 | } 297 | ``` 298 | 299 | # Обучающий проект 300 | 301 | [Обучающий проект](https://github.com/laphedhendad/UI-Framework-Example) можно скачать архивом с GitHub. Проект использует [Zenject](https://github.com/modesttree/Zenject) и [UniTask](https://github.com/Cysharp/UniTask) в достаточно небольшом количестве, чтобы не отрывать внимание от пакета. 302 | -------------------------------------------------------------------------------- /README_RU.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 96a31cdcd9d27e443b8c6c0cacdba122 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6a6cc77c7ff932d4b863773a168dbbd1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 925d78ab36a66f04f92a6301481c9bcd 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Editor/Laphed.UIFramework.UnityBridge.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Laphed.UIFramework.Editor", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:38531a8a6cccc1e40929c5761ceb76a9" 6 | ], 7 | "includePlatforms": [ 8 | "Editor" 9 | ], 10 | "excludePlatforms": [], 11 | "allowUnsafeCode": false, 12 | "overrideReferences": false, 13 | "precompiledReferences": [], 14 | "autoReferenced": true, 15 | "defineConstraints": [], 16 | "versionDefines": [], 17 | "noEngineReferences": false 18 | } -------------------------------------------------------------------------------- /Scripts/Editor/Laphed.UIFramework.UnityBridge.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 164e0e3a6c2ae5f4e8651beb1ec40c8e 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scripts/Editor/WindowCustomInspector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using UnityEditor; 4 | using UnityEngine; 5 | 6 | namespace UIFramework.Editor 7 | { 8 | [CustomEditor(typeof(MonoWindow))] 9 | public class WindowCustomInspector: UnityEditor.Editor 10 | { 11 | public override void OnInspectorGUI() 12 | { 13 | DrawDefaultInspector(); 14 | 15 | MonoWindow window = (MonoWindow)target; 16 | 17 | GUILayout.Space(10); 18 | 19 | if (GUILayout.Button("Find subwindows automatically")) 20 | { 21 | FindSubwindows(window); 22 | GUIUtility.ExitGUI(); 23 | } 24 | } 25 | 26 | private void FindSubwindows(MonoWindow window) 27 | { 28 | List subwindows = window.GetComponentsInChildren(true).ToList(); 29 | subwindows.RemoveAt(0); 30 | foreach (MonoWindow subwindow in subwindows) 31 | { 32 | FindSubwindows(subwindow); 33 | } 34 | window.SetSubwindows(subwindows.ToArray()); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Scripts/Editor/WindowCustomInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f30b03a379b544f498af871983902a6a 3 | timeCreated: 1705389824 -------------------------------------------------------------------------------- /Scripts/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7653890c0f9128d4b9b0bd192e0546ff 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Runtime/MVP.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 574fae0da5d9a6d449d2b6e52ef9a84b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/CollectionPresenter.cs: -------------------------------------------------------------------------------- 1 | using Laphed.Rx; 2 | 3 | namespace Laphed.MVP 4 | { 5 | public abstract class CollectionPresenter: ReactivePresenterBase> 6 | { 7 | protected CollectionPresenter(IView view): base(view) 8 | { 9 | } 10 | 11 | public override void SubscribeModel(IReadonlyReactiveCollection model) 12 | { 13 | base.SubscribeModel(model); 14 | UpdateView(model); 15 | } 16 | 17 | protected override void HandleModelUpdate() => UpdateView(model); 18 | protected abstract void UpdateView(IReadonlyReactiveCollection model); 19 | } 20 | 21 | public class CollectionPresenter: CollectionPresenter> 22 | { 23 | protected CollectionPresenter(IView> view): base(view) 24 | { 25 | } 26 | 27 | protected override void UpdateView(IReadonlyReactiveCollection model) => view.UpdateView(model); 28 | } 29 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/CollectionPresenter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7bb02344b9e14aea8d01755018060655 3 | timeCreated: 1703237882 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/DictionaryPresenter.cs: -------------------------------------------------------------------------------- 1 | using Laphed.Rx.Dictionary; 2 | 3 | namespace Laphed.MVP 4 | { 5 | public abstract class DictionaryPresenter: ReactivePresenterBase> 6 | { 7 | protected DictionaryPresenter(IView view) : base(view) 8 | { 9 | } 10 | 11 | public override void SubscribeModel(IReadOnlyReactiveDictionary model) 12 | { 13 | base.SubscribeModel(model); 14 | UpdateView(model); 15 | } 16 | 17 | protected override void HandleModelUpdate() => UpdateView(model); 18 | 19 | protected abstract void UpdateView(IReadOnlyReactiveDictionary model); 20 | } 21 | 22 | public class DictionaryPresenter: DictionaryPresenter> 23 | { 24 | protected DictionaryPresenter(IView> view) : base(view) 25 | { 26 | } 27 | 28 | protected override void HandleModelUpdate() => UpdateView(model); 29 | 30 | protected override void UpdateView(IReadOnlyReactiveDictionary model) => view.UpdateView(model); 31 | } 32 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/DictionaryPresenter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e5bc65ef6b24fd0a1219034472bff12 3 | timeCreated: 1704888174 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/IPresenter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laphed.MVP 4 | { 5 | public interface IPresenter: IDisposable 6 | { 7 | void SubscribeModel(TModel model); 8 | } 9 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/IPresenter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 123cdd37e4164bb0aa97ce250ae9d279 3 | timeCreated: 1704269146 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/IView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laphed.MVP 4 | { 5 | public interface IView 6 | { 7 | event Action OnDispose; 8 | void UpdateView(T value); 9 | } 10 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/IView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8a1b1ee46a8b4bc6846f5c68367f7f2d 3 | timeCreated: 1696392261 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/Laphed.MVP.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Laphed.MVP", 3 | "rootNamespace": "Laphed.MVP", 4 | "references": [ 5 | "GUID:cdda196ad70c47f40ae7a6aff190a032" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/Laphed.MVP.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 849c4a899d48346459c9f919a37f6310 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/MonoBehaviourViews.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 27eb687adee141d5b22fd7f8f32e3150 3 | timeCreated: 1703076227 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/MonoBehaviourViews/MonoSpriteView.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Laphed.MVP.MonoViews 4 | { 5 | public abstract class MonoSpriteView: MonoView 6 | { 7 | protected abstract Sprite Sprite { set; } 8 | public override void UpdateView(Sprite value) => Sprite = value; 9 | } 10 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/MonoBehaviourViews/MonoSpriteView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5c6e676cb7ec40a48e5ffb5c65d27d3f 3 | timeCreated: 1703075808 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/MonoBehaviourViews/MonoTextView.cs: -------------------------------------------------------------------------------- 1 | namespace Laphed.MVP.MonoViews 2 | { 3 | public abstract class MonoTextView: MonoView 4 | { 5 | protected abstract string Text { set; } 6 | public override void UpdateView(string value) => Text = value; 7 | } 8 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/MonoBehaviourViews/MonoTextView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 334eb62891774959a4683e1670a55575 3 | timeCreated: 1703076332 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/MonoBehaviourViews/MonoView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace Laphed.MVP.MonoViews 5 | { 6 | public abstract class MonoView: MonoBehaviour, IView 7 | { 8 | public event Action OnDispose; 9 | public abstract void UpdateView(T value); 10 | protected virtual void OnDestroy() => OnDispose?.Invoke(); 11 | } 12 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/MonoBehaviourViews/MonoView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ff4039f459ed463dae7252ebdea5044d 3 | timeCreated: 1703076348 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/PresenterBase.cs: -------------------------------------------------------------------------------- 1 | using Laphed.Rx; 2 | 3 | namespace Laphed.MVP 4 | { 5 | public abstract class PresenterBase: IPresenter where TReactive: IReactive 6 | { 7 | protected IView view; 8 | protected TReactive model; 9 | 10 | protected PresenterBase(IView view) 11 | { 12 | this.view = view; 13 | view.OnDispose += Dispose; 14 | } 15 | 16 | public abstract void SubscribeModel(TReactive model); 17 | 18 | protected abstract void HandleModelUpdate(); 19 | 20 | public virtual void Dispose() 21 | { 22 | view.OnDispose -= Dispose; 23 | UnsubscribeModel(); 24 | } 25 | 26 | protected abstract void UnsubscribeModel(); 27 | } 28 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/PresenterBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8fe54c57f47a45148d4c396d3cad4da9 3 | timeCreated: 1704268821 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/PropertyPresenter.cs: -------------------------------------------------------------------------------- 1 | using Laphed.Rx; 2 | 3 | namespace Laphed.MVP 4 | { 5 | public abstract class PropertyPresenter: ReactivePresenterBase> 6 | { 7 | protected PropertyPresenter(IView view): base(view) 8 | { 9 | } 10 | 11 | public override void SubscribeModel(IReactiveProperty model) 12 | { 13 | base.SubscribeModel(model); 14 | UpdateView(model.Value); 15 | } 16 | 17 | protected override void HandleModelUpdate() => UpdateView(model.Value); 18 | 19 | protected abstract void UpdateView(TModelData value); 20 | } 21 | 22 | public class PropertyPresenter: PropertyPresenter 23 | { 24 | public PropertyPresenter(IView view) : base(view) 25 | { 26 | } 27 | 28 | protected override void UpdateView(TData value) => view.UpdateView(value); 29 | } 30 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/PropertyPresenter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1600b1c4df3947e5a15c6aef7eea6d65 3 | timeCreated: 1696392796 -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/ReactivePresenterBase.cs: -------------------------------------------------------------------------------- 1 | using Laphed.Rx; 2 | 3 | namespace Laphed.MVP 4 | { 5 | public abstract class ReactivePresenterBase: PresenterBase where TReactive: IReactive 6 | { 7 | protected ReactivePresenterBase(IView view): base(view) 8 | { 9 | view.OnDispose += Dispose; 10 | } 11 | 12 | public override void SubscribeModel(TReactive model) 13 | { 14 | if (model == null) return; 15 | this.model = model; 16 | model.OnChanged += HandleModelUpdate; 17 | } 18 | 19 | protected override void UnsubscribeModel() 20 | { 21 | if (model == null) return; 22 | model.OnChanged -= HandleModelUpdate; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Scripts/Runtime/MVP/ReactivePresenterBase.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 588b3e088a6548e489ad64c7922a23e3 3 | timeCreated: 1703239369 -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 56d02f3bfbfe8ba409227b458c8285c7 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/IUiBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UIFramework 4 | { 5 | public interface IUiBinder: IDisposable 6 | { 7 | void Bind(); 8 | } 9 | } -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/IUiBinder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 385775f80dd842ea8ac0785cae433329 3 | timeCreated: 1704907891 -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/IWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UIFramework 4 | { 5 | public interface IWindow: IDisposable 6 | { 7 | void Initialize(); 8 | } 9 | } -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/IWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dbd03902cf6c40f09104f683612027dd 3 | timeCreated: 1704970286 -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/Laphed.UIFramework.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Laphed.UIFramework", 3 | "rootNamespace": "Laphed.UIFramework", 4 | "references": [ 5 | "GUID:cdda196ad70c47f40ae7a6aff190a032" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/Laphed.UIFramework.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38531a8a6cccc1e40929c5761ceb76a9 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/UiBinder.cs: -------------------------------------------------------------------------------- 1 | namespace UIFramework 2 | { 3 | public abstract class UiBinder: IUiBinder 4 | { 5 | private bool disposed; 6 | 7 | public abstract void Bind(); 8 | 9 | protected abstract void Unbind(); 10 | 11 | public void Dispose() => Dispose(true); 12 | 13 | protected virtual void Dispose(bool disposing) 14 | { 15 | if (disposed) return; 16 | Unbind(); 17 | disposed = true; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/UiBinder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a2ddc999069d482b9707ac8ecb577dfb 3 | timeCreated: 1704907881 -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/UnityMonoBridge.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 69a80367b379466ba6c6c33202ee643e 3 | timeCreated: 1704914255 -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/UnityMonoBridge/MonoBinder.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UIFramework 4 | { 5 | public abstract class MonoBinder: MonoBehaviour, IUiBinder 6 | { 7 | private bool disposed; 8 | 9 | public abstract void Bind(); 10 | 11 | protected abstract void Unbind(); 12 | 13 | public void Dispose() => Dispose(true); 14 | 15 | protected virtual void Dispose(bool disposing) 16 | { 17 | if (disposed) return; 18 | Unbind(); 19 | disposed = true; 20 | } 21 | 22 | protected virtual void OnDestroy() 23 | { 24 | Dispose(false); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/UnityMonoBridge/MonoBinder.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 15969e50146c49c5be9b23a973f389b1 3 | timeCreated: 1705388700 -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/UnityMonoBridge/MonoWindow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UIFramework 4 | { 5 | public class MonoWindow: MonoBehaviour, IWindow 6 | { 7 | [SerializeField] private MonoBinder binder; 8 | [SerializeField] private MonoWindow[] subwindows; 9 | 10 | private bool isInitialized; 11 | 12 | public void Initialize() 13 | { 14 | if (isInitialized) return; 15 | 16 | isInitialized = true; 17 | binder.Bind(); 18 | 19 | foreach (MonoWindow window in subwindows) 20 | { 21 | window.Initialize(); 22 | } 23 | } 24 | 25 | public void SetSubwindows(MonoWindow[] subwindows) => this.subwindows = subwindows; 26 | 27 | #region Dispose Pattern 28 | private bool disposed; 29 | 30 | public void Dispose() 31 | { 32 | Dispose(true); 33 | Destroy(this); 34 | } 35 | 36 | private void Dispose(bool disposing) 37 | { 38 | if (disposed) return; 39 | 40 | if (disposing) 41 | { 42 | foreach (MonoWindow window in subwindows) 43 | { 44 | window.Dispose(); 45 | } 46 | 47 | subwindows = null; 48 | } 49 | 50 | binder.Dispose(); 51 | binder = null; 52 | disposed = true; 53 | } 54 | 55 | private void OnDestroy() 56 | { 57 | Dispose(false); 58 | } 59 | #endregion 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/UnityMonoBridge/MonoWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 252b19f9ceca498188168532dbf453fa 3 | timeCreated: 1705389027 -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/Window.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace UIFramework 4 | { 5 | public class Window: IWindow 6 | { 7 | private IUiBinder binder; 8 | private IWindow[] subwindows; 9 | private bool isInitialized; 10 | 11 | public Window(IUiBinder binder) 12 | { 13 | this.binder = binder; 14 | subwindows = Array.Empty(); 15 | } 16 | 17 | public Window(IUiBinder binder, IWindow[] subwindows): this(binder) 18 | { 19 | this.subwindows = subwindows; 20 | } 21 | 22 | public void Initialize() 23 | { 24 | if (isInitialized) return; 25 | 26 | isInitialized = true; 27 | binder.Bind(); 28 | 29 | foreach (IWindow window in subwindows) 30 | { 31 | window.Initialize(); 32 | } 33 | } 34 | 35 | #region Dispose Pattern 36 | private bool disposed; 37 | 38 | public void Dispose() 39 | { 40 | Dispose(true); 41 | GC.SuppressFinalize(this); 42 | } 43 | 44 | protected virtual void Dispose(bool disposing) 45 | { 46 | if (disposed) return; 47 | 48 | if (disposing) 49 | { 50 | foreach (IWindow window in subwindows) 51 | { 52 | window.Dispose(); 53 | } 54 | 55 | subwindows = null; 56 | } 57 | 58 | binder.Dispose(); 59 | binder = null; 60 | disposed = true; 61 | } 62 | 63 | ~Window() 64 | { 65 | Dispose(false); 66 | } 67 | #endregion 68 | } 69 | } -------------------------------------------------------------------------------- /Scripts/Runtime/UIFramework/Window.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0f205c2d67ac4d03a426165f34901ad9 3 | timeCreated: 1704894519 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.laphed.ui-framework", 3 | "displayName": "UI Framework", 4 | "author": { "name": "Laphed Hendad", "url": "https://github.com/laphedhendad" }, 5 | "version": "1.0.1", 6 | "unity": "2020.1", 7 | "description": "UI Framework for Unity based on reactivity and MVP pattern.", 8 | "keywords": [ "ui", "mvp"], 9 | "license": "MIT", 10 | "category": "UI", 11 | "dependencies": { 12 | } 13 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c10364fa4544ee3939370684ec4b7b2 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------