├── .gitignore ├── LICENSE ├── ProjectGroup1.groupproj ├── README-en.md ├── README.md ├── Router4Delphi.dpk ├── Router4Delphi.dproj ├── Samples ├── FMX │ ├── Demo │ │ ├── Router4DelphiDemo.View.Principal.fmx │ │ ├── Router4DelphiDemo.View.Principal.pas │ │ ├── Router4DelphiDemo.dpr │ │ ├── Router4DelphiDemo.dproj │ │ ├── Router4DelphiDemo.res │ │ └── Views │ │ │ ├── Components │ │ │ ├── Router4DelphiDemo.View.Components.Sidebar.fmx │ │ │ └── Router4DelphiDemo.View.Components.Sidebar.pas │ │ │ ├── Layouts │ │ │ ├── Router4DelphiDemo.Views.Layouts.Main.fmx │ │ │ └── Router4DelphiDemo.Views.Layouts.Main.pas │ │ │ ├── Pages │ │ │ ├── Router4DelphiDemo.View.Pages.Cadastros.fmx │ │ │ ├── Router4DelphiDemo.View.Pages.Cadastros.pas │ │ │ ├── Router4DelphiDemo.View.Pages.Index.fmx │ │ │ └── Router4DelphiDemo.View.Pages.Index.pas │ │ │ ├── Router4DelphiDemo.View.Principal.fmx │ │ │ ├── Router4DelphiDemo.View.Principal.pas │ │ │ └── Routers │ │ │ └── Router4DelphiDemo.View.Router.pas │ └── SimpleDemo │ │ ├── SimpleDemo.View.Components.Button01.fmx │ │ ├── SimpleDemo.View.Components.Button01.pas │ │ ├── SimpleDemo.View.Page.Cadastros.Sub.fmx │ │ ├── SimpleDemo.View.Page.Cadastros.Sub.pas │ │ ├── SimpleDemo.View.Page.Cadastros.fmx │ │ ├── SimpleDemo.View.Page.Cadastros.pas │ │ ├── SimpleDemo.View.Page.Principal.fmx │ │ ├── SimpleDemo.View.Page.Principal.pas │ │ ├── SimpleDemo.View.Principal.fmx │ │ ├── SimpleDemo.View.Principal.pas │ │ ├── SimpleDemo.dpr │ │ ├── SimpleDemo.dproj │ │ └── SimpleDemo.res └── VCL │ ├── Main.dfm │ ├── Main.pas │ ├── Router.dpr │ ├── Router.dproj │ └── pages │ ├── View.Page.Customer.dfm │ ├── View.Page.Customer.pas │ ├── View.Page.Main.Cadastro.dfm │ ├── View.Page.Main.Cadastro.pas │ ├── View.Page.Product.dfm │ ├── View.Page.Product.pas │ ├── View.Page.Template.dfm │ └── View.Page.Template.pas ├── assets └── logo.fw.png ├── boss-lock.json ├── boss.json └── src ├── DuckListU.pas ├── EventBus.Core.pas ├── EventBus.Subscribers.pas ├── ObjectsMappers.pas ├── RTTIUtilsU.pas ├── Router4D.Helper.pas ├── Router4D.History.pas ├── Router4D.Interfaces.pas ├── Router4D.Link.pas ├── Router4D.Props.pas ├── Router4D.Render.pas ├── Router4D.Sidebar.pas ├── Router4D.Switch.pas ├── Router4D.Utils.pas ├── Router4D.inc └── Router4D.pas /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | *.res 7 | 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | 68 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss 69 | modules/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Thulio Bittencourt 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 | -------------------------------------------------------------------------------- /ProjectGroup1.groupproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {9C8C8331-996E-4D6D-AEBB-F1D465788ABF} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Default.Personality.12 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | router4d 4 | 5 |

6 |
7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

16 | 17 | # Router4Delphi 18 | Framework for Creating Screen Routes for FMX and VCL 19 | 20 | Router4Delphi aims to facilitate the calling of screens and embed Layouts in FMX applications, and Panels in VCL applications, reducing the coupling of screens and providing more dynamism and practicality in the construction of rich Delphi interfaces. 21 | 22 | ## Instalation 23 | 24 | Simply register the path to the library's SRC folder in the Library Path of your Delphi, or if you prefer, you can use [**Boss**](https://github.com/HashLoad/boss) (dependency manager for Delphi) To perform the installation: 25 | ``` 26 | boss install https://github.com/academiadocodigo/Router4Delphi 27 | ``` 28 | 29 | ## First Steps - Tutorial 30 | 31 | To use Router4Delphi to create your routes, you must use Router4D. 32 | 33 | #### Observation 34 | 35 | Inside the src folder contains Router4D.inc, this file has the compilation directive for Firemonkey, with this directive commented out the Framework will have VCL support, and when uncommenting it you will have FMX support. 36 | 37 | ## Creating a Screen for Routing 38 | 39 | For the Route system to work you must create a new FMX or VCL form and Implement the iRouter4DComponent Interface, it belongs to the Router4D.Interfaces unit so it must be included in your Units. 40 | 41 | All route-based screen construction uses TLayouts and TPanels to embed the screen calls, so your new screen must have a TLayout or a main TPanel and all other components must be included within this layout or panel. 42 | 43 | The iRouter4DComponent Interface Implementation requires the declaration of two methods (Render and UnRender), Render is called whenever a route activates the screen, and UnRender whenever it leaves the display. 44 | 45 | Below is the code for a simple screen implementing the iRouter4DComponent interface and ready to be used. 46 | 47 | #### FMX Sample 48 | 49 | Create a New Form in your Application, include an AlClient aligned Layout in it and implement the methods as below. 50 | 51 | ```delphi 52 | 53 | unit PrimeiraTela; 54 | 55 | interface 56 | 57 | uses 58 | System.SysUtils, 59 | System.Types, 60 | System.UITypes, 61 | System.Classes, 62 | System.Variants, 63 | FMX.Types, 64 | FMX.Controls, 65 | FMX.Forms, 66 | FMX.Graphics, 67 | FMX.Dialogs, 68 | Router4D.Interfaces; 69 | 70 | type 71 | TFirstScreen = class(TForm, iRouter4DComponent) 72 | Layout1: TLayout; 73 | private 74 | { Private declarations } 75 | public 76 | { Public declarations } 77 | function Render : TFMXObject; 78 | procedure UnRender; 79 | end; 80 | 81 | var 82 | FirstScreen: TFirstScreen; 83 | 84 | implementation 85 | 86 | {$R *.fmx} 87 | 88 | { TForm3 } 89 | 90 | function TFirstScreen.Render: TFMXObject; 91 | begin 92 | Result := Layout1; 93 | end; 94 | 95 | procedure TFirstScreen.UnRender; 96 | begin 97 | 98 | end; 99 | 100 | end. 101 | ``` 102 | 103 | Note that in the Render method we define Layout1 as Result, this is necessary because this layout will be embedded whenever the route is activated. 104 | 105 | ## Registering the Route to the Screen 106 | 107 | Now that we have a screen ready to be registered, let's move on to the process that will make our screen ready to be activated at any time. 108 | 109 | To register a route it is necessary to declare Uses Router4D, it provides access to all library methods and in many cases it will be the only coupling necessary in your Views. 110 | 111 | Once declared, simply activate the method below to declare the form we created previously as a route. 112 | 113 | In the Main form of your Application, within the onCreate method, execute the method below to register the Route for the Form TFirstScreen 114 | 115 | ```delphi 116 | 117 | procedure TMainForm.FormCreate(Sender: TObject); 118 | begin 119 | TRouter4D.Switch.Router('Inicio', TFirstScreen); 120 | end; 121 | ``` 122 | 123 | We now have a Route created, so our forms no longer need to know the uses of our screen, just activate our route system and ask for the creation of the "Start" route and it will be displayed in the application's LayoutMain. 124 | 125 | You can create a separate Unit just to Register the Routes or call a method in the onCreate of your main form for this. 126 | 127 | ## Defining the Main Render 128 | 129 | We already have a screen and a route to use, now we just need to define where this route will render the layout, that is, what will be our Object that will receive the embedded screens. 130 | 131 | To do this, in the formPrincipal of your application, declare uses Router4D and in its onCreate make the following call. 132 | 133 | Remembering that in the previous step we had already used the onCreate formPrincipal to Register the Route. 134 | 135 | ```delphi 136 | 137 | procedure TMainForm.FormCreate(Sender: TObject); 138 | begin 139 | TRouter4D.Switch.Router('Inicio', TFirstScreen); 140 | 141 | TRouter4D.Render.SetElement(Layout1, Layout1); 142 | end; 143 | 144 | ``` 145 | 146 | The Render method is responsible for defining the Application's LayoutsMain and Index in the library. 147 | 148 | The Render receives the name of the Class from our home screen as a generic, it will be rendered when the application opens within the Layout that was informed as the first parameter of the SetElement 149 | 150 | The first parameter of the SetElement is defining in which Layout the library will render a new screen whenever a Route Link is called. 151 | 152 | The second parameter of the SetElement is defining the Index layout of the application, so when an IndexLink is called it will be rendered in that layout, later I will explain about the IndexLink. 153 | 154 | Okay, now when you open your application you will already have the TPrimeiraTela Form Layout being rendered within Layout1 of your application's Main form. 155 | 156 | ## Creating a Second Screen 157 | 158 | So that we can actually see the component in action and all its benefits, create a new screen similar to the one we did at the beginning, adding an alClient Layout to it and implementing the Render and UnRender methods. 159 | 160 | Place a Label inside the Layout, for example, written on the second screen, just to make sure everything worked correctly. 161 | 162 | ```delphi 163 | 164 | unit SecondScreen; 165 | 166 | interface 167 | 168 | uses 169 | System.SysUtils, 170 | System.Types, 171 | System.UITypes, 172 | System.Classes, 173 | System.Variants, 174 | FMX.Types, 175 | FMX.Controls, 176 | FMX.Forms, 177 | FMX.Graphics, 178 | FMX.Dialogs, 179 | Router4D.Interfaces; 180 | 181 | type 182 | TSecondScreen = class(TForm, iRouter4DComponent) 183 | Layout1: TLayout; 184 | private 185 | { Private declarations } 186 | public 187 | { Public declarations } 188 | function Render : TFMXObject; 189 | procedure UnRender; 190 | end; 191 | 192 | var 193 | SecondScreen: TSecondScreen; 194 | 195 | implementation 196 | 197 | {$R *.fmx} 198 | 199 | { TSecondScreen } 200 | 201 | function TSecondScreen.Render: TFMXObject; 202 | begin 203 | Result := Layout1; 204 | end; 205 | 206 | procedure TSecondScreen.UnRender; 207 | begin 208 | 209 | end; 210 | 211 | end. 212 | ``` 213 | ## Registering the Second Screen on the Route 214 | 215 | Now that we have created a new screen we need to register it in the Routes system, so let's go back to onCreate and make this registration, let's call this screen Screen2. 216 | 217 | ```delphi 218 | 219 | procedure TMainForm.FormCreate(Sender: TObject); 220 | begin 221 | TRouter4D.Switch.Router('Inicio', TFirstScreen); 222 | 223 | TRouter4D.Switch.Router('Tela2', TSecondScreen); 224 | 225 | TRouter4D.Render.SetElement(Layout1, Layout1); 226 | end; 227 | 228 | ``` 229 | 230 | 231 | ## Activating the new screen through the Route using the Link 232 | 233 | Now that the magic comes, go back to the TFirstScreen and place a button there and we will use the Router4D Links system to call the TSegundaScreen without having to use it. 234 | 235 | Just call the method below in the Button Click Event. 236 | 237 | ```delphi 238 | procedure TFirstScreen.Button1Click(Sender: TObject); 239 | begin 240 | TRouter4D.Link.&To('Tela2'); 241 | end; 242 | ``` 243 | 244 | Note that TFirstScreen does not know TSecondScren because its uses were only given in the formPrincipal where it is necessary for Route Registration. 245 | 246 | If you want to make this more organized, I even suggest that you create a separate Unit just to register the Routes with a class procedure and call this method in the MainForm's onCreate. 247 | 248 | This way we put an end to a lot of cross-references and coupling between screens. 249 | 250 | 251 | ## Resources - Render 252 | 253 | ```delphi 254 | TRouter4D.Render.SetElement(MainContainer, IndexContainer); 255 | ``` 256 | 257 | Render is the first action to be done to work with Router4D, as in it you will configure the main and index containers. 258 | 259 | MainContainer = The container where the forms will be embedded 260 | 261 | IndexContainer = The main container of the application (useful when you have more than one type of layout in the application) 262 | 263 | ## SWITCH 264 | 265 | ```delphi 266 | TRouter4D.Switch.Router(aPath : String; aRouter : TPersistentClass); 267 | ``` 268 | On the Switch you register your routes, passing the name of the route and the object that is opened when this route is activated. 269 | 270 | ```delphi 271 | TRouter4D.Switch.Router(aPath : String; aRouter : TPersistentClass; aSidebarKey : String = 'SBIndex'; isVisible : Boolean = True); 272 | ``` 273 | 274 | In Swith there are some additional parameters that already have default values 275 | 276 | aSidebarKey: This parameter allows you to separate routes by category for creating dynamic menus with the SideBar class, I will explain more about it below. 277 | 278 | isVisible: Allows you to hide the route when dynamically generating menus with SideBar. 279 | 280 | ## LINK 281 | 282 | ```delphi 283 | 284 | TRouter4D.Link.&To ( aPatch : String; aComponent : TFMXObject ); 285 | 286 | TRouter4D.Link.&To ( aPatch : String); 287 | 288 | TRouter4D.Link.&To ( aPatch : String; aProps : TProps; aKey : String = ''); 289 | 290 | ``` 291 | 292 | Links are the actions to trigger the routes you registered on the Switch 293 | 294 | There are 3 ways to call the links: 295 | 296 | ```delphi 297 | TRouter4D.Link.&To ( aPatch : String); 298 | ``` 299 | Passing only the Route Path, this way the form associated with the route will be embedded within the MainContainer that you defined in Render 300 | 301 | ```delphi 302 | TRouter4D.Link.&To ( aPatch : String; aComponent : TFMXObject ); 303 | ``` 304 | 305 | Passing the Path and the Component, it will embed the form registered in the path within the component that you are passing in the parameter. 306 | 307 | ```delphi 308 | TRouter4D.Link.&To ( aPatch : String; aProps : TProps; aKey : String = ''); 309 | ``` 310 | 311 | You can trigger a route by passing Props, which are values that your form will receive at the time of Render, I will explain below how this works in detail, but this is useful for example when you want to send an ID to a screen to perform a query in the database and be loaded with the data. 312 | 313 | ## PROPS 314 | 315 | ```delphi 316 | TRouter4D.Link.&To ( aPatch : String; aProps : TProps; aKey : String = ''); 317 | ``` 318 | 319 | The Router4D Library incorporates the Delphi Event Bus to perform Pub and Sub actions, so you can register your forms to receive events when calling links. 320 | 321 | To receive a Props you need to add uses Router4D.Props to your form and implement the following method with the [Subscribe] attribute 322 | 323 | ```delphi 324 | [Subscribe] 325 | procedure Props ( aValue : TProps); 326 | ``` 327 | 328 | and implement it 329 | 330 | ```delphi 331 | procedure TPageCadastros.Props(aValue: TProps); 332 | begin 333 | if aValue.Key = 'telacadastro' then 334 | Label1.Text := aValue.PropString; 335 | aValue.Free; 336 | end; 337 | ``` 338 | This way, your form is prepared, for example, to receive a string passed in the link call. 339 | 340 | To call a link by passing a Props you use the following code: 341 | 342 | ```delphi 343 | TRouter4D.Link.&To('Cadastros', TProps.Create.PropString('Olá').Key('telacadastro')); 344 | ``` 345 | Passing the TProps object with a PropString and a Key to the Link so that the receiving screen can be sure that that prop was sent to it. 346 | 347 | ## SideBar 348 | 349 | With registered routes you can create an automatic menu of registered routes dynamically, simply register a new route and it will be available in all your menus. 350 | 351 | ```delphi 352 | TRouter4D 353 | .SideBar 354 | .MainContainer(Layout5) 355 | .LinkContainer(Layout4) 356 | .FontSize(15) 357 | .FontColor(4294967295) 358 | .ItemHeigth(60) 359 | .RenderToListBox; 360 | ``` 361 | 362 | In the example above we are generating a menu in listbox format within Layout5 and all links clicked on this menu will be rendered in Layout4, if you do not pass the LinkContainer it will be rendered in the MainContainer informed in the Router4D Render. 363 | 364 | You can also create menus based on categorized routes, just enter the category in which the route belongs when registering the route. 365 | 366 | ```delphi 367 | TRouter4D.Switch.Router('Clientes', TPagePrincipal, 'cadastros'); 368 | TRouter4D.Switch.Router('Fornecedores', TSubCadastros, 'cadastros'); 369 | TRouter4D.Switch.Router('Produtos', TSubCadastros, 'cadastros'); 370 | ``` 371 | 372 | This way we created 3 routes in the registration category. To generate a menu with just these links, simply inform this when building the SideBar. 373 | 374 | ```delphi 375 | TRouter4D 376 | .SideBar 377 | .Name('cadastros') 378 | .MainContainer(Layout5) 379 | .LinkContainer(Layout4) 380 | .FontSize(15) 381 | .FontColor(4294967295) 382 | .ItemHeigth(60) 383 | .RenderToListBox; 384 | ``` 385 | ## Documentação 386 | [English (en)](https://github.com/academiadocodigo/Router4Delphi/blob/master/README-en.md)
387 | [Português (ptBR)](https://github.com/academiadocodigo/Router4Delphi/blob/master/README.md)
388 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | router4d 4 | 5 |

6 |
7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

16 | 17 | # Router4Delphi 18 | Framework para Criação de Rotas de Telas para FMX e VCL 19 | 20 | O Router4Delphi tem o objetivo de facilitar a chamada de telas e embed de Layouts em aplicações FMX, e Panels em aplicações VCL, reduzindo o acoplamento das telas de dando mais dinâmismo e práticidade na construção de interfaces ricas em Delphi 21 | 22 | ## Instalação 23 | 24 | Basta registrar no Library Path do seu Delphi o caminho da pasta SRC da biblioteca, ou se preferir, pode utilizar o [**Boss**](https://github.com/HashLoad/boss) (gerenciador de dependências para Delphi) para realizar a instalação: 25 | ``` 26 | boss install https://github.com/bittencourtthulio/Router4Delphi 27 | ``` 28 | 29 | ## Primeiros Passos - Tutorial 30 | 31 | Para utilizar o Router4Delphi para criar suas rotas, você deve realizar a uses do Router4D. 32 | 33 | #### Observação 34 | 35 | Dentro da pasta src contém o Router4D.inc, esse arquivo possui a diretiva de compilação para o Firemonkey, com essa diretiva comentada o Framework terá suporte a VCL, e ao descomentar você terá suporte ao FMX. 36 | 37 | ## Criação de uma Tela para Roteamento 38 | 39 | Para que o sistema de Rotas funcione você deve criar um novo formulário FMX ou VCL e Implementar a Interface iRouter4DComponent ela pertence a unit Router4D.Interfaces portanto a mesma deve ser incluida nas suas Units. 40 | 41 | Toda a construção das telas baseadas em rotas utilizar TLayouts e TPanels para embedar as chamadas das telas, dessa forma é preciso que sua nova tela tenha um TLayout ou um TPanel principal e todos os demais componentes devem ser incluídos dentro desse layout ou panel. 42 | 43 | A Implementação da Interface iRouter4DComponent requer a declaração de dois métodos ( Render e UnRender ), o Render é chamado sempre que uma rota aciona a tela, e o UnRender sempre que ela saí de exibição. 44 | 45 | Abaixo o Código de uma tela simples implementando a interface iRouter4DComponent e pronta para ser utilizada. 46 | 47 | #### Exemplo em FMX 48 | 49 | Crie um Novo Formulario na sua Aplicação, inclua nele um Layout alinhado AlClient e implemente os métodos como abaixo. 50 | 51 | ```delphi 52 | 53 | unit PrimeiraTela; 54 | 55 | interface 56 | 57 | uses 58 | System.SysUtils, 59 | System.Types, 60 | System.UITypes, 61 | System.Classes, 62 | System.Variants, 63 | FMX.Types, 64 | FMX.Controls, 65 | FMX.Forms, 66 | FMX.Graphics, 67 | FMX.Dialogs, 68 | Router4D.Interfaces; 69 | 70 | type 71 | TPrimeiraTela = class(TForm, iRouter4DComponent) 72 | Layout1: TLayout; 73 | private 74 | { Private declarations } 75 | public 76 | { Public declarations } 77 | function Render : TFMXObject; 78 | procedure UnRender; 79 | end; 80 | 81 | var 82 | PrimeiraTela: TPrimeiraTela; 83 | 84 | implementation 85 | 86 | {$R *.fmx} 87 | 88 | { TForm3 } 89 | 90 | function TPrimeiraTela.Render: TFMXObject; 91 | begin 92 | Result := Layout1; 93 | end; 94 | 95 | procedure TPrimeiraTela.UnRender; 96 | begin 97 | 98 | end; 99 | 100 | end. 101 | ``` 102 | 103 | Perceba que no método Render nós definimos como Result o Layout1, isso é necessário pois esse layout será embedado sempre que a rota for acionada. 104 | 105 | ## Registrando a Rota para a Tela 106 | 107 | Agora que já temos uma tela pronta para ser registrada vamos ao processo que deixará a nossa tela pronta para ser acionada a qualquer momento. 108 | 109 | Para registrar uma rota é necessário declarar a Uses Router4D ela fornece acesso a todos os métodos da biblioteca e em muito dos casos será o único acoplamento necessário nas suas Views. 110 | 111 | Uma vez declarada basta acionar o método abaixo para declarar o form que criamos anteriormente como uma rota. 112 | 113 | No formPrincipal da sua Aplicação, dentro do método onCreate execute o método abaixo para registrar a Rota para o Form TPrimeiraTela 114 | 115 | ```delphi 116 | 117 | procedure TformPrincipal.FormCreate(Sender: TObject); 118 | begin 119 | TRouter4D.Switch.Router('Inicio', TPrimeiraTela); 120 | end; 121 | ``` 122 | 123 | Pronto já temos uma Rota criada, dessa forma os nossos forms não precisam mais conhecer a uses da nossa tela, basta acionar nosso sistema de rotas e pedir a criação da rota "Inicio" que a mesma será exibida no LayoutMain da aplicação. 124 | 125 | Você pode criar uma Unit Separada somente para Registrar as Rotas ou então chamar um metodo no onCreate do seu formulario principal para isso. 126 | 127 | ## Definindo o Render Principal 128 | 129 | Já temos uma tela e uma rota para utilizarmos, agora precisamos definir apenas onde está rota renderizará o layout, ou seja, qual será o nosso Objeto que vai receber as telas embedadas. 130 | 131 | Para isso no formPrincipal da sua aplicação, declare a uses Router4D e no onCreate do mesmo faça a seguinte chamada. 132 | 133 | Lembrando que no passo anterios nós já tinhamos usado o onCreate do formPrincipal para Registrar a Rota. 134 | 135 | ```delphi 136 | 137 | procedure TformPrincipal.FormCreate(Sender: TObject); 138 | begin 139 | TRouter4D.Switch.Router('Inicio', TPrimeiraTela); 140 | 141 | TRouter4D.Render.SetElement(Layout1, Layout1); 142 | end; 143 | 144 | ``` 145 | 146 | O método Render é responsável por definir na biblioteca quais serão os LayoutsMain e Index da Aplicação. 147 | 148 | O Render recebe como genéric o nome da Classe da nossa tela inicial, ela será renderizada quando a aplicação abrir dentro do Layout que foi informado como primeiro parametro do SetElement 149 | 150 | O primeiro parametro do SetElement está definindo em qual Layout a biblioteca irá renderizar uma nova tela sempre que um Link da rota for chamado. 151 | 152 | O Segundo parametro do SetElement está definindo qual é o layout Index da aplicação, assim quando um IndexLink for chamado ele será renderizado nesse layout, mais para frente explicarei sobre o IndexLink. 153 | 154 | Pronto, agora ao abrir a sua aplicação você já terá o Layout do Formulario TPrimeiraTela sendo renderizado dentro do Layout1 do formPrincipal da sua aplicação. 155 | 156 | ## Criando uma Segunda Tela 157 | 158 | Para que possamos ver o componente em ação de fato e todos os seus benefícios, crie uma nova tela semelhante a que fizemos no inicio, adicionando um Layout alClient nela e implementando os métodos Render e UnRender. 159 | 160 | Coloque dentro do Layout um Label por exemplo, escrito segunda tela apenas para termos a certeza que tudo funcionou corretamente. 161 | 162 | ```delphi 163 | 164 | unit SegundaTela; 165 | 166 | interface 167 | 168 | uses 169 | System.SysUtils, 170 | System.Types, 171 | System.UITypes, 172 | System.Classes, 173 | System.Variants, 174 | FMX.Types, 175 | FMX.Controls, 176 | FMX.Forms, 177 | FMX.Graphics, 178 | FMX.Dialogs, 179 | Router4D.Interfaces; 180 | 181 | type 182 | TSegundaTela = class(TForm, iRouter4DComponent) 183 | Layout1: TLayout; 184 | private 185 | { Private declarations } 186 | public 187 | { Public declarations } 188 | function Render : TFMXObject; 189 | procedure UnRender; 190 | end; 191 | 192 | var 193 | SegundaTela: TSegundaTela; 194 | 195 | implementation 196 | 197 | {$R *.fmx} 198 | 199 | { TSegundaTela } 200 | 201 | function TSegundaTela.Render: TFMXObject; 202 | begin 203 | Result := Layout1; 204 | end; 205 | 206 | procedure TSegundaTela.UnRender; 207 | begin 208 | 209 | end; 210 | 211 | end. 212 | ``` 213 | ## Registrando a Segunda tela na Rota 214 | 215 | Agora que criamos uma nova tela precisamos registrar ela no sistema de Rotas, então vamos voltar ao onCreate e fazer esse registros, vamos chamar essa tela de Tela2. 216 | 217 | ```delphi 218 | 219 | procedure TformPrincipal.FormCreate(Sender: TObject); 220 | begin 221 | TRouter4D.Switch.Router('Inicio', TPrimeiraTela); 222 | 223 | TRouter4D.Switch.Router('Tela2', TSegundaTela); 224 | 225 | TRouter4D.Render.SetElement(Layout1, Layout1); 226 | end; 227 | 228 | ``` 229 | 230 | 231 | ## Acionando a nova tela atráves da Rota utilizando o Link 232 | 233 | Agora que vem a mágica, volte na TPrimeiraTela e coloque um botão lá e vamos usar o sistema de Links do Router4D para chamar a TSegundaTela sem precisar dar uses nela. 234 | 235 | Basta chamar o método abaixo no Evento de Clique do Botão. 236 | 237 | ```delphi 238 | procedure TPrimeiraTela.Button1Click(Sender: TObject); 239 | begin 240 | TRouter4D.Link.&To('Tela2'); 241 | end; 242 | ``` 243 | 244 | Perceba que a TPrimeiraTela não conhece a TSegundaTela pois o uses da mesma foi dado apenas no formPrincipal onde é necessário para o Registro das Rotas. 245 | 246 | Se você deseja deixar isso mais organizado, eu sugiro inclusive que você crie uma Unit separada apenas para registro das Rotas com um class procedure e faça a chamada desse método no onCreate do formPrincipal. 247 | 248 | Dessa forma damos fim a um monte de referencias cruzadas e acoplamento entre as telas. 249 | 250 | 251 | ## RECURSOS - RENDER 252 | 253 | ```delphi 254 | TRouter4D.Render.SetElement(MainContainer, IndexContainer); 255 | ``` 256 | 257 | O Render é a primeira ação a ser feita para trabalhar com o Router4D, pois nele você irá configurar os container main e index. 258 | 259 | MainContainer = O container onde os formularios serão embedados 260 | 261 | IndexContainer = O container principal da aplicação (util quando você tem mais de um tipo de layout na aplicação) 262 | 263 | ## SWITCH 264 | 265 | ```delphi 266 | TRouter4D.Switch.Router(aPath : String; aRouter : TPersistentClass); 267 | ``` 268 | No Switch você registra suas rotas, passando o nome da rota e o objeto que seja aberto quando essa rota for acionada. 269 | 270 | ```delphi 271 | TRouter4D.Switch.Router(aPath : String; aRouter : TPersistentClass; aSidebarKey : String = 'SBIndex'; isVisible : Boolean = True); 272 | ``` 273 | 274 | No Swith existem alguns parametros a mais que já possuem valores default 275 | 276 | aSidebarKey: Este parametro permite você separar as rotas por categoria para a criação de menus dinâmicos com a classe SideBar, vou explicar mais abaixo sobre ela. 277 | 278 | isVisible: Permite você ocultar a rota na geração dinamica dos menus com a SideBar. 279 | 280 | ## LINK 281 | 282 | ```delphi 283 | 284 | TRouter4D.Link.&To ( aPatch : String; aComponent : TFMXObject ); 285 | 286 | TRouter4D.Link.&To ( aPatch : String); 287 | 288 | TRouter4D.Link.&To ( aPatch : String; aProps : TProps; aKey : String = ''); 289 | 290 | ``` 291 | 292 | Os links são as ações para acionar as rotas que você registrou no Switch 293 | 294 | Existem 3 formas de chamar os links: 295 | 296 | ```delphi 297 | TRouter4D.Link.&To ( aPatch : String); 298 | ``` 299 | Passando apenas o Path da Rota, dessa forma o formulario associado a rota será embedado dentro do MainContainer que você definiu no Render 300 | 301 | ```delphi 302 | TRouter4D.Link.&To ( aPatch : String; aComponent : TFMXObject ); 303 | ``` 304 | 305 | Passando o Path e o Component, ele irá embedar o formulario registrado no path dentro do componente que você está passando no parametro. 306 | 307 | ```delphi 308 | TRouter4D.Link.&To ( aPatch : String; aProps : TProps; aKey : String = ''); 309 | ``` 310 | 311 | Você pode acionar uma rota passando Props, que são valores que o seu formulário irá receber no momento do Render, vou explicar mais abaixo como isso funciona em detalhes, mas isso é util por exemplo quando você deseja enviar um ID para uma tela realizar uma consulta no banco e ser carregada com os dados. 312 | 313 | ## PROPS 314 | 315 | ```delphi 316 | TRouter4D.Link.&To ( aPatch : String; aProps : TProps; aKey : String = ''); 317 | ``` 318 | 319 | A Biblioteca Router4D incopora o Delphi Event Bus para realizar ações de Pub e Sub, com isso você pode registrar seus formularios para receber eventos na chamada dos links. 320 | 321 | Para receber uma Props você precisa adicionar a uses Router4D.Props no seu formulario e implementar o seguinte método com o atributo [Subscribe] 322 | 323 | ```delphi 324 | [Subscribe] 325 | procedure Props ( aValue : TProps); 326 | ``` 327 | 328 | e implementa-lo 329 | 330 | ```delphi 331 | procedure TPageCadastros.Props(aValue: TProps); 332 | begin 333 | if aValue.Key = 'telacadastro' then 334 | Label1.Text := aValue.PropString; 335 | aValue.Free; 336 | end; 337 | ``` 338 | Dessa forma seu formulario está preparado por exemplo para receber uma string passada na chamada do link. 339 | 340 | Para chamar um link passando um Props você utiliza o seguinte código: 341 | 342 | ```delphi 343 | TRouter4D.Link.&To('Cadastros', TProps.Create.PropString('Olá').Key('telacadastro')); 344 | ``` 345 | Passando no Link o objeto TProps com uma PropString e uma Chave para que a tela que vai receber tenha certeza que aquela props foi enviada para ela. 346 | 347 | ## SideBar 348 | 349 | Com as rotas registradas você pode criar um menu automático das rotas registradas de forma dinâmica, basta registrar uma nova rota que a mesma estará disponível em todos os seus menus. 350 | 351 | ```delphi 352 | TRouter4D 353 | .SideBar 354 | .MainContainer(Layout5) 355 | .LinkContainer(Layout4) 356 | .FontSize(15) 357 | .FontColor(4294967295) 358 | .ItemHeigth(60) 359 | .RenderToListBox; 360 | ``` 361 | 362 | No exemplo acima estamos gerando um menu em formato de listbox dentro do Layout5 e todos os links clicados nesse menu serão renderizados no Layout4, se você não passar o LinkContainer o mesmo será renderizado no MainContainer informado no Render do Router4D. 363 | 364 | Você ainda pode criar menus baseados em rotas categorizadas, basta no registro da rota você informar a categoria que a rota pertence 365 | 366 | ```delphi 367 | TRouter4D.Switch.Router('Clientes', TPagePrincipal, 'cadastros'); 368 | TRouter4D.Switch.Router('Fornecedores', TSubCadastros, 'cadastros'); 369 | TRouter4D.Switch.Router('Produtos', TSubCadastros, 'cadastros'); 370 | ``` 371 | 372 | Dessa forma criamos 3 rotas da categoria cadastro, para gerar um menu apenas com esses link basta informar isso na construção da SideBar. 373 | 374 | ```delphi 375 | TRouter4D 376 | .SideBar 377 | .Name('cadastros') 378 | .MainContainer(Layout5) 379 | .LinkContainer(Layout4) 380 | .FontSize(15) 381 | .FontColor(4294967295) 382 | .ItemHeigth(60) 383 | .RenderToListBox; 384 | ``` 385 | ## Documentação 386 | [English (en)](https://github.com/academiadocodigo/Router4Delphi/blob/master/README-en.md)
387 | [Português (ptBR)](https://github.com/academiadocodigo/Router4Delphi/blob/master/README.md)
388 | -------------------------------------------------------------------------------- /Router4Delphi.dpk: -------------------------------------------------------------------------------- 1 | package Router4Delphi; 2 | 3 | {$R *.res} 4 | {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} 5 | {$ALIGN 8} 6 | {$ASSERTIONS ON} 7 | {$BOOLEVAL OFF} 8 | {$DEBUGINFO OFF} 9 | {$EXTENDEDSYNTAX ON} 10 | {$IMPORTEDDATA ON} 11 | {$IOCHECKS ON} 12 | {$LOCALSYMBOLS ON} 13 | {$LONGSTRINGS ON} 14 | {$OPENSTRINGS ON} 15 | {$OPTIMIZATION OFF} 16 | {$OVERFLOWCHECKS OFF} 17 | {$RANGECHECKS OFF} 18 | {$REFERENCEINFO ON} 19 | {$SAFEDIVIDE OFF} 20 | {$STACKFRAMES ON} 21 | {$TYPEDADDRESS OFF} 22 | {$VARSTRINGCHECKS ON} 23 | {$WRITEABLECONST OFF} 24 | {$MINENUMSIZE 1} 25 | {$IMAGEBASE $400000} 26 | {$DEFINE DEBUG} 27 | {$ENDIF IMPLICITBUILDING} 28 | {$IMPLICITBUILD ON} 29 | 30 | requires 31 | rtl, 32 | xmlrtl, 33 | fmx, 34 | soaprtl, 35 | dbrtl, 36 | DbxCommonDriver, 37 | FireDAC, 38 | FireDACCommonDriver, 39 | FireDACCommon, 40 | vcl; 41 | 42 | contains 43 | DuckListU in 'src\DuckListU.pas', 44 | EventBus.Core in 'src\EventBus.Core.pas', 45 | EventBus.Subscribers in 'src\EventBus.Subscribers.pas', 46 | ObjectsMappers in 'src\ObjectsMappers.pas', 47 | Router4D.History in 'src\Router4D.History.pas', 48 | Router4D.Interfaces in 'src\Router4D.Interfaces.pas', 49 | Router4D.Link in 'src\Router4D.Link.pas', 50 | Router4D in 'src\Router4D.pas', 51 | Router4D.Props in 'src\Router4D.Props.pas', 52 | Router4D.Render in 'src\Router4D.Render.pas', 53 | Router4D.Switch in 'src\Router4D.Switch.pas', 54 | Router4D.Utils in 'src\Router4D.Utils.pas', 55 | RTTIUtilsU in 'src\RTTIUtilsU.pas', 56 | Router4D.Helper in 'src\Router4D.Helper.pas', 57 | Router4D.Sidebar in 'src\Router4D.Sidebar.pas'; 58 | 59 | end. 60 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Router4DelphiDemo.View.Principal.fmx: -------------------------------------------------------------------------------- 1 | object Form2: TForm2 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form2' 5 | ClientHeight = 537 6 | ClientWidth = 921 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | DesignerMasterStyle = 0 11 | end 12 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Router4DelphiDemo.View.Principal.pas: -------------------------------------------------------------------------------- 1 | unit Router4DelphiDemo.View.Principal; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs; 8 | 9 | type 10 | TForm2 = class(TForm) 11 | private 12 | { Private declarations } 13 | public 14 | { Public declarations } 15 | end; 16 | 17 | var 18 | Form2: TForm2; 19 | 20 | implementation 21 | 22 | {$R *.fmx} 23 | 24 | end. 25 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Router4DelphiDemo.dpr: -------------------------------------------------------------------------------- 1 | program Router4DelphiDemo; 2 | 3 | uses 4 | System.StartUpCopy, 5 | FMX.Forms, 6 | Router4DelphiDemo.View.Principal in 'Views\Router4DelphiDemo.View.Principal.pas' {ViewPrincipal}, 7 | Router4DelphiDemo.Views.Layouts.Main in 'Views\Layouts\Router4DelphiDemo.Views.Layouts.Main.pas' {MainLayout}, 8 | Router4DelphiDemo.View.Components.Sidebar in 'Views\Components\Router4DelphiDemo.View.Components.Sidebar.pas' {ComponentSideBar}, 9 | Router4DelphiDemo.View.Router in 'Views\Routers\Router4DelphiDemo.View.Router.pas', 10 | Router4DelphiDemo.View.Pages.Index in 'Views\Pages\Router4DelphiDemo.View.Pages.Index.pas' {PageIndex}, 11 | Router4DelphiDemo.View.Pages.Cadastros in 'Views\Pages\Router4DelphiDemo.View.Pages.Cadastros.pas' {PageCadastros}; 12 | 13 | {$R *.res} 14 | 15 | begin 16 | Application.Initialize; 17 | Application.CreateForm(TViewPrincipal, ViewPrincipal); 18 | Application.Run; 19 | end. 20 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Router4DelphiDemo.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academiadocodigo/Router4Delphi/eedd012e4b68cbda69067f9baf3f9ccfb628abc6/Samples/FMX/Demo/Router4DelphiDemo.res -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Components/Router4DelphiDemo.View.Components.Sidebar.fmx: -------------------------------------------------------------------------------- 1 | object ComponentSideBar: TComponentSideBar 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form4' 5 | ClientHeight = 480 6 | ClientWidth = 640 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | DesignerMasterStyle = 0 11 | object Layout1: TLayout 12 | Align = Client 13 | Size.Width = 640.000000000000000000 14 | Size.Height = 480.000000000000000000 15 | Size.PlatformDefault = False 16 | object Rectangle1: TRectangle 17 | Align = Contents 18 | Fill.Color = xFF36414A 19 | Size.Width = 640.000000000000000000 20 | Size.Height = 480.000000000000000000 21 | Size.PlatformDefault = False 22 | Stroke.Kind = None 23 | end 24 | object ListBox1: TListBox 25 | Align = Client 26 | Size.Width = 640.000000000000000000 27 | Size.Height = 480.000000000000000000 28 | Size.PlatformDefault = False 29 | StyleLookup = 'transparentlistboxstyle' 30 | OnClick = ListBox1Click 31 | DisableFocusEffect = True 32 | ItemHeight = 60.000000000000000000 33 | DefaultItemStyles.ItemStyle = '' 34 | DefaultItemStyles.GroupHeaderStyle = '' 35 | DefaultItemStyles.GroupFooterStyle = '' 36 | Viewport.Width = 640.000000000000000000 37 | Viewport.Height = 480.000000000000000000 38 | object ListBoxItem1: TListBoxItem 39 | TextSettings.Font.Size = 15.000000000000000000 40 | TextSettings.FontColor = claWhite 41 | StyledSettings = [Family, Style, Other] 42 | Padding.Left = 15.000000000000000000 43 | Size.Width = 640.000000000000000000 44 | Size.Height = 60.000000000000000000 45 | Size.PlatformDefault = False 46 | Text = 'Home' 47 | end 48 | object ListBoxItem2: TListBoxItem 49 | TextSettings.Font.Size = 15.000000000000000000 50 | TextSettings.FontColor = claWhite 51 | StyledSettings = [Family, Style, Other] 52 | Position.Y = 60.000000000000000000 53 | Size.Width = 640.000000000000000000 54 | Size.Height = 60.000000000000000000 55 | Size.PlatformDefault = False 56 | Text = 'Cadastros' 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Components/Router4DelphiDemo.View.Components.Sidebar.pas: -------------------------------------------------------------------------------- 1 | unit Router4DelphiDemo.View.Components.Sidebar; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Layouts, 8 | FMX.ListBox, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects; 9 | 10 | type 11 | TComponentSideBar = class(TForm) 12 | Layout1: TLayout; 13 | ListBox1: TListBox; 14 | ListBoxItem1: TListBoxItem; 15 | ListBoxItem2: TListBoxItem; 16 | Rectangle1: TRectangle; 17 | procedure ListBox1Click(Sender: TObject); 18 | private 19 | { Private declarations } 20 | public 21 | { Public declarations } 22 | end; 23 | 24 | var 25 | ComponentSideBar: TComponentSideBar; 26 | 27 | implementation 28 | 29 | uses 30 | Router4D; 31 | 32 | {$R *.fmx} 33 | 34 | procedure TComponentSideBar.ListBox1Click(Sender: TObject); 35 | begin 36 | TRouter4D.Link.&To(ListBox1.Items[ListBox1.ItemIndex]) 37 | end; 38 | 39 | end. 40 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Layouts/Router4DelphiDemo.Views.Layouts.Main.fmx: -------------------------------------------------------------------------------- 1 | object MainLayout: TMainLayout 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form3' 5 | ClientHeight = 577 6 | ClientWidth = 860 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | DesignerMasterStyle = 0 11 | object LayoutIndex: TLayout 12 | Align = Contents 13 | Size.Width = 860.000000000000000000 14 | Size.Height = 577.000000000000000000 15 | Size.PlatformDefault = False 16 | object Layout1: TLayout 17 | Align = Top 18 | Size.Width = 860.000000000000000000 19 | Size.Height = 65.000000000000000000 20 | Size.PlatformDefault = False 21 | object Rectangle1: TRectangle 22 | Align = Contents 23 | Fill.Color = xFF2D2F32 24 | Size.Width = 860.000000000000000000 25 | Size.Height = 65.000000000000000000 26 | Size.PlatformDefault = False 27 | Stroke.Kind = None 28 | end 29 | object Label1: TLabel 30 | Align = Left 31 | StyledSettings = [Family, Style] 32 | Margins.Left = 15.000000000000000000 33 | Position.X = 15.000000000000000000 34 | Size.Width = 554.000000000000000000 35 | Size.Height = 65.000000000000000000 36 | Size.PlatformDefault = False 37 | TextSettings.Font.Size = 25.000000000000000000 38 | TextSettings.FontColor = claWhite 39 | Text = 'Layout Principal' 40 | end 41 | end 42 | object Layout2: TLayout 43 | Align = Left 44 | Position.Y = 65.000000000000000000 45 | Size.Width = 225.000000000000000000 46 | Size.Height = 512.000000000000000000 47 | Size.PlatformDefault = False 48 | end 49 | object Layout3: TLayout 50 | Align = Client 51 | Size.Width = 635.000000000000000000 52 | Size.Height = 512.000000000000000000 53 | Size.PlatformDefault = False 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Layouts/Router4DelphiDemo.Views.Layouts.Main.pas: -------------------------------------------------------------------------------- 1 | unit Router4DelphiDemo.Views.Layouts.Main; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, 7 | System.Types, 8 | System.UITypes, 9 | System.Classes, 10 | System.Variants, 11 | FMX.Types, 12 | FMX.Controls, 13 | FMX.Forms, 14 | FMX.Graphics, 15 | FMX.Dialogs, 16 | FMX.Layouts, 17 | FMX.Controls.Presentation, 18 | FMX.StdCtrls, 19 | Router4D.Interfaces, FMX.Objects; 20 | 21 | type 22 | TMainLayout = class(TForm, iRouter4DComponent) 23 | Layout1: TLayout; 24 | Layout2: TLayout; 25 | Layout3: TLayout; 26 | Label1: TLabel; 27 | LayoutIndex: TLayout; 28 | Rectangle1: TRectangle; 29 | private 30 | { Private declarations } 31 | public 32 | { Public declarations } 33 | function Render : TFMXObject; 34 | procedure UnRender; 35 | end; 36 | 37 | var 38 | MainLayout: TMainLayout; 39 | 40 | implementation 41 | 42 | uses 43 | Router4DelphiDemo.View.Pages.Index, 44 | Router4D, 45 | Router4DelphiDemo.View.Components.Sidebar; 46 | 47 | {$R *.fmx} 48 | 49 | { TMainLayout } 50 | 51 | function TMainLayout.Render: TFMXObject; 52 | begin 53 | Result := LayoutIndex; 54 | TRouter4D.Render.SetElement(Layout3); 55 | 56 | Layout2.RemoveObject(0); 57 | Layout2.AddObject( 58 | TComponentSideBar.Create(Self).Layout1 59 | ) 60 | end; 61 | 62 | procedure TMainLayout.UnRender; 63 | begin 64 | 65 | end; 66 | 67 | end. 68 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Pages/Router4DelphiDemo.View.Pages.Cadastros.fmx: -------------------------------------------------------------------------------- 1 | object PageCadastros: TPageCadastros 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form2' 5 | ClientHeight = 480 6 | ClientWidth = 640 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | DesignerMasterStyle = 0 11 | object Layout1: TLayout 12 | Align = Client 13 | Size.Width = 640.000000000000000000 14 | Size.Height = 480.000000000000000000 15 | Size.PlatformDefault = False 16 | object Label1: TLabel 17 | Align = Client 18 | StyledSettings = [Family, Style, FontColor] 19 | Size.Width = 640.000000000000000000 20 | Size.Height = 480.000000000000000000 21 | Size.PlatformDefault = False 22 | TextSettings.Font.Size = 30.000000000000000000 23 | TextSettings.HorzAlign = Center 24 | Text = 'Cadastros' 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Pages/Router4DelphiDemo.View.Pages.Cadastros.pas: -------------------------------------------------------------------------------- 1 | unit Router4DelphiDemo.View.Pages.Cadastros; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, 8 | FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, 9 | Router4D.Interfaces; 10 | 11 | type 12 | TPageCadastros = class(TForm, iRouter4DComponent) 13 | Layout1: TLayout; 14 | Label1: TLabel; 15 | private 16 | { Private declarations } 17 | public 18 | { Public declarations } 19 | function Render : TFMXObject; 20 | procedure UnRender; 21 | end; 22 | 23 | var 24 | PageCadastros: TPageCadastros; 25 | 26 | implementation 27 | 28 | {$R *.fmx} 29 | 30 | { TForm2 } 31 | 32 | function TPageCadastros.Render: TFMXObject; 33 | begin 34 | Result := Layout1; 35 | end; 36 | 37 | procedure TPageCadastros.UnRender; 38 | begin 39 | 40 | end; 41 | 42 | end. 43 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Pages/Router4DelphiDemo.View.Pages.Index.fmx: -------------------------------------------------------------------------------- 1 | object PageIndex: TPageIndex 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form5' 5 | ClientHeight = 609 6 | ClientWidth = 940 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | DesignerMasterStyle = 0 11 | object Layout1: TLayout 12 | Align = Contents 13 | Size.Width = 940.000000000000000000 14 | Size.Height = 609.000000000000000000 15 | Size.PlatformDefault = False 16 | object Label1: TLabel 17 | Align = Client 18 | StyledSettings = [Family, Style, FontColor] 19 | Size.Width = 940.000000000000000000 20 | Size.Height = 609.000000000000000000 21 | Size.PlatformDefault = False 22 | TextSettings.Font.Size = 30.000000000000000000 23 | TextSettings.HorzAlign = Center 24 | Text = 'Home' 25 | end 26 | object Button1: TButton 27 | Position.X = 424.000000000000000000 28 | Position.Y = 408.000000000000000000 29 | TabOrder = 0 30 | Text = 'Button1' 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Pages/Router4DelphiDemo.View.Pages.Index.pas: -------------------------------------------------------------------------------- 1 | unit Router4DelphiDemo.View.Pages.Index; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, 7 | System.Types, 8 | System.UITypes, 9 | System.Classes, 10 | System.Variants, 11 | FMX.Types, 12 | FMX.Controls, 13 | FMX.Forms, 14 | FMX.Graphics, 15 | FMX.Dialogs, 16 | FMX.Layouts, 17 | Router4D.Interfaces, FMX.Controls.Presentation, FMX.StdCtrls; 18 | 19 | type 20 | TPageIndex = class(TForm, iRouter4DComponent) 21 | Layout1: TLayout; 22 | Label1: TLabel; 23 | Button1: TButton; 24 | private 25 | { Private declarations } 26 | public 27 | { Public declarations } 28 | function Render : TFMXObject; 29 | procedure UnRender; 30 | end; 31 | 32 | var 33 | PageIndex: TPageIndex; 34 | 35 | implementation 36 | 37 | uses 38 | Router4D, 39 | Router4D.History, 40 | Router4DelphiDemo.Views.Layouts.Main; 41 | 42 | {$R *.fmx} 43 | 44 | function TPageIndex.Render: TFMXObject; 45 | begin 46 | Result := Layout1; 47 | //TRouter4D.Render.GetElement(Layout1); 48 | end; 49 | 50 | procedure TPageIndex.UnRender; 51 | begin 52 | 53 | end; 54 | 55 | end. 56 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Router4DelphiDemo.View.Principal.fmx: -------------------------------------------------------------------------------- 1 | object ViewPrincipal: TViewPrincipal 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form2' 5 | ClientHeight = 612 6 | ClientWidth = 925 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | OnCreate = FormCreate 11 | DesignerMasterStyle = 0 12 | object Layout1: TLayout 13 | Align = Contents 14 | Size.Width = 925.000000000000000000 15 | Size.Height = 612.000000000000000000 16 | Size.PlatformDefault = False 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Router4DelphiDemo.View.Principal.pas: -------------------------------------------------------------------------------- 1 | unit Router4DelphiDemo.View.Principal; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Layouts; 8 | 9 | type 10 | TViewPrincipal = class(TForm) 11 | Layout1: TLayout; 12 | procedure FormCreate(Sender: TObject); 13 | private 14 | { Private declarations } 15 | public 16 | { Public declarations } 17 | procedure Animation( aLayout : TFMXObject ); 18 | end; 19 | 20 | var 21 | ViewPrincipal: TViewPrincipal; 22 | 23 | implementation 24 | 25 | uses 26 | Router4D, 27 | Router4DelphiDemo.Views.Layouts.Main, 28 | Router4DelphiDemo.View.Router; 29 | 30 | {$R *.fmx} 31 | 32 | procedure TViewPrincipal.Animation(aLayout: TFMXObject); 33 | begin 34 | TLayout(aLayout).Opacity := 0; 35 | TLayout(aLayout).AnimateFloat('Opacity', 1, 0.9); 36 | end; 37 | 38 | procedure TViewPrincipal.FormCreate(Sender: TObject); 39 | begin 40 | TRouter4D.Render.SetElement(Layout1, Layout1); 41 | TRouter4D.Link.Animation(Animation); 42 | end; 43 | 44 | end. 45 | -------------------------------------------------------------------------------- /Samples/FMX/Demo/Views/Routers/Router4DelphiDemo.View.Router.pas: -------------------------------------------------------------------------------- 1 | unit Router4DelphiDemo.View.Router; 2 | 3 | interface 4 | 5 | type 6 | TRouters = class 7 | private 8 | public 9 | constructor Create; 10 | destructor Destroy; override; 11 | end; 12 | 13 | var 14 | Routers : TRouters; 15 | 16 | implementation 17 | 18 | uses 19 | Router4D, 20 | Router4DelphiDemo.View.Pages.Index, 21 | Router4DelphiDemo.Views.Layouts.Main, 22 | Router4DelphiDemo.View.Pages.Cadastros; 23 | 24 | { TRouters } 25 | 26 | constructor TRouters.Create; 27 | begin 28 | TRouter4D.Switch.Router('Home', TPageIndex); 29 | TRouter4D.Switch.Router('Cadastros', TPageCadastros); 30 | TRouter4D.Switch.Router('main', TMainLayout); 31 | end; 32 | 33 | destructor TRouters.Destroy; 34 | begin 35 | 36 | inherited; 37 | end; 38 | 39 | initialization 40 | Routers := TRouters.Create; 41 | 42 | finalization 43 | Routers.Free; 44 | 45 | end. 46 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Components.Button01.fmx: -------------------------------------------------------------------------------- 1 | object ComponentButton01: TComponentButton01 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form1' 5 | ClientHeight = 480 6 | ClientWidth = 640 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | OnCreate = FormCreate 11 | DesignerMasterStyle = 0 12 | object Layout1: TLayout 13 | Position.X = 232.000000000000000000 14 | Position.Y = 120.000000000000000000 15 | Size.Width = 121.000000000000000000 16 | Size.Height = 81.000000000000000000 17 | Size.PlatformDefault = False 18 | object Line1: TLine 19 | Align = Bottom 20 | LineType = Bottom 21 | Position.Y = 80.000000000000000000 22 | Size.Width = 121.000000000000000000 23 | Size.Height = 1.000000000000000000 24 | Size.PlatformDefault = False 25 | Stroke.Thickness = 5.000000000000000000 26 | end 27 | object Label1: TLabel 28 | Align = Contents 29 | StyledSettings = [Family, Style, FontColor] 30 | Size.Width = 121.000000000000000000 31 | Size.Height = 81.000000000000000000 32 | Size.PlatformDefault = False 33 | TextSettings.Font.Size = 20.000000000000000000 34 | TextSettings.HorzAlign = Center 35 | Text = 'Button' 36 | end 37 | object SpeedButton1: TSpeedButton 38 | Align = Contents 39 | Opacity = 0.000000000000000000 40 | Size.Width = 121.000000000000000000 41 | Size.Height = 81.000000000000000000 42 | Size.PlatformDefault = False 43 | Text = 'SpeedButton1' 44 | OnClick = SpeedButton1Click 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Components.Button01.pas: -------------------------------------------------------------------------------- 1 | unit SimpleDemo.View.Components.Button01; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, 8 | FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects, FMX.Layouts, 9 | Router4D.Interfaces, 10 | Router4D.Props; 11 | 12 | type 13 | TComponentButton01 = class(TForm, iRouter4DComponent) 14 | Layout1: TLayout; 15 | Line1: TLine; 16 | Label1: TLabel; 17 | SpeedButton1: TSpeedButton; 18 | procedure FormCreate(Sender: TObject); 19 | procedure SpeedButton1Click(Sender: TObject); 20 | private 21 | { Private declarations } 22 | public 23 | { Public declarations } 24 | function Render : TFMXObject; 25 | procedure UnRender; 26 | [Subscribe] 27 | procedure Props ( aValue : TProps); 28 | function createButton(aLabel : String) : TFMXObject; 29 | end; 30 | 31 | var 32 | ComponentButton01: TComponentButton01; 33 | 34 | implementation 35 | 36 | {$R *.fmx} 37 | 38 | { TComponentButton01 } 39 | 40 | function TComponentButton01.createButton(aLabel: String): TFMXObject; 41 | begin 42 | Result := Layout1; 43 | Label1.Text := aLabel; 44 | Layout1.Align := TAlignLayout.Left; 45 | Line1.Visible := False; 46 | Self.TagString := aLabel; 47 | end; 48 | 49 | procedure TComponentButton01.FormCreate(Sender: TObject); 50 | begin 51 | GlobalEventBus.RegisterSubscriber(Self); 52 | end; 53 | 54 | procedure TComponentButton01.Props(aValue: TProps); 55 | begin 56 | Line1.Visible := False; 57 | 58 | if (aValue.PropString = Label1.Text) and 59 | (aValue.Key = 'Button01') then 60 | Line1.Visible := True; 61 | 62 | aValue.Free; 63 | end; 64 | 65 | function TComponentButton01.Render: TFMXObject; 66 | begin 67 | Result := Layout1; 68 | end; 69 | 70 | procedure TComponentButton01.SpeedButton1Click(Sender: TObject); 71 | begin 72 | Line1.Visible := True; 73 | GlobalEventBus.Post( 74 | TProps.Create 75 | .PropString(Label1.Text) 76 | .Key('Button01') 77 | ); 78 | end; 79 | 80 | procedure TComponentButton01.UnRender; 81 | begin 82 | // 83 | end; 84 | 85 | end. 86 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Page.Cadastros.Sub.fmx: -------------------------------------------------------------------------------- 1 | object SubCadastros: TSubCadastros 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form1' 5 | ClientHeight = 480 6 | ClientWidth = 640 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | DesignerMasterStyle = 0 11 | object Layout1: TLayout 12 | Align = Client 13 | Size.Width = 640.000000000000000000 14 | Size.Height = 480.000000000000000000 15 | Size.PlatformDefault = False 16 | object Label1: TLabel 17 | Align = Contents 18 | StyledSettings = [Family, Style, FontColor] 19 | Size.Width = 640.000000000000000000 20 | Size.Height = 480.000000000000000000 21 | Size.PlatformDefault = False 22 | TextSettings.Font.Size = 30.000000000000000000 23 | TextSettings.HorzAlign = Center 24 | Text = 'Sub-Cadastros' 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Page.Cadastros.Sub.pas: -------------------------------------------------------------------------------- 1 | unit SimpleDemo.View.Page.Cadastros.Sub; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, 8 | FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, 9 | Router4D.Interfaces; 10 | 11 | type 12 | TSubCadastros = class(TForm, iRouter4DComponent) 13 | Layout1: TLayout; 14 | Label1: TLabel; 15 | private 16 | { Private declarations } 17 | public 18 | { Public declarations } 19 | function Render : TFMXObject; 20 | procedure UnRender; 21 | end; 22 | 23 | var 24 | SubCadastros: TSubCadastros; 25 | 26 | implementation 27 | 28 | uses 29 | Router4D.History; 30 | 31 | {$R *.fmx} 32 | 33 | { TSubCadastros } 34 | 35 | function TSubCadastros.Render: TFMXObject; 36 | begin 37 | Result := Layout1; 38 | end; 39 | 40 | procedure TSubCadastros.UnRender; 41 | begin 42 | // 43 | end; 44 | 45 | end. 46 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Page.Cadastros.fmx: -------------------------------------------------------------------------------- 1 | object PageCadastros: TPageCadastros 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form3' 5 | ClientHeight = 480 6 | ClientWidth = 640 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | OnCreate = FormCreate 11 | DesignerMasterStyle = 0 12 | object Layout1: TLayout 13 | Align = Client 14 | Size.Width = 640.000000000000000000 15 | Size.Height = 480.000000000000000000 16 | Size.PlatformDefault = False 17 | object Layout2: TLayout 18 | Align = Left 19 | Size.Width = 177.000000000000000000 20 | Size.Height = 480.000000000000000000 21 | Size.PlatformDefault = False 22 | Visible = False 23 | object Rectangle1: TRectangle 24 | Align = Contents 25 | Fill.Color = xFF36414A 26 | Size.Width = 177.000000000000000000 27 | Size.Height = 480.000000000000000000 28 | Size.PlatformDefault = False 29 | Stroke.Kind = None 30 | end 31 | end 32 | object Layout3: TLayout 33 | Align = Client 34 | Size.Width = 640.000000000000000000 35 | Size.Height = 399.000000000000000000 36 | Size.PlatformDefault = False 37 | object Label1: TLabel 38 | Align = Contents 39 | StyledSettings = [Family, Style, FontColor] 40 | Size.Width = 640.000000000000000000 41 | Size.Height = 399.000000000000000000 42 | Size.PlatformDefault = False 43 | TextSettings.Font.Size = 30.000000000000000000 44 | TextSettings.HorzAlign = Center 45 | Text = 'Cadastros' 46 | end 47 | object Button1: TButton 48 | Anchors = [] 49 | Position.X = 243.682922363281300000 50 | Position.Y = 215.990631103515600000 51 | Size.Width = 145.000000000000000000 52 | Size.Height = 41.000000000000000000 53 | Size.PlatformDefault = False 54 | Text = 'Voltar para Home' 55 | OnClick = Button1Click 56 | end 57 | object Edit1: TEdit 58 | Touch.InteractiveGestures = [LongTap, DoubleTap] 59 | Anchors = [] 60 | Position.X = 243.682922363281300000 61 | Position.Y = 257.240631103515600000 62 | Size.Width = 145.000000000000000000 63 | Size.Height = 25.000000000000000000 64 | Size.PlatformDefault = False 65 | end 66 | end 67 | object Layout4: TLayout 68 | Align = Top 69 | Size.Width = 640.000000000000000000 70 | Size.Height = 81.000000000000000000 71 | Size.PlatformDefault = False 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Page.Cadastros.pas: -------------------------------------------------------------------------------- 1 | unit SimpleDemo.View.Page.Cadastros; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, 7 | System.Types, 8 | System.UITypes, 9 | System.Classes, 10 | System.Variants, 11 | FMX.Types, 12 | FMX.Controls, 13 | FMX.Forms, 14 | FMX.Graphics, 15 | FMX.Dialogs, 16 | FMX.Controls.Presentation, 17 | FMX.StdCtrls, 18 | FMX.Layouts, 19 | Router4D.Interfaces, 20 | Router4D.Props, FMX.Edit, FMX.Objects; 21 | 22 | type 23 | TPageCadastros = class(TForm, iRouter4DComponent) 24 | Layout1: TLayout; 25 | Label1: TLabel; 26 | Button1: TButton; 27 | Edit1: TEdit; 28 | Layout2: TLayout; 29 | Layout3: TLayout; 30 | Rectangle1: TRectangle; 31 | Layout4: TLayout; 32 | procedure Button1Click(Sender: TObject); 33 | procedure FormCreate(Sender: TObject); 34 | private 35 | procedure CreateMenuSuperior; 36 | procedure CreateRouters; 37 | { Private declarations } 38 | public 39 | { Public declarations } 40 | function Render : TFMXObject; 41 | procedure UnRender; 42 | [Subscribe] 43 | procedure Props ( aValue : TProps); 44 | end; 45 | 46 | var 47 | PageCadastros: TPageCadastros; 48 | 49 | implementation 50 | 51 | uses 52 | Router4D, SimpleDemo.View.Page.Cadastros.Sub, SimpleDemo.View.Page.Principal, 53 | SimpleDemo.View.Components.Button01; 54 | 55 | {$R *.fmx} 56 | 57 | { TPageCadastros } 58 | 59 | procedure TPageCadastros.Button1Click(Sender: TObject); 60 | begin 61 | TRouter4D.Link.&To('Inicio'); 62 | end; 63 | 64 | procedure TPageCadastros.FormCreate(Sender: TObject); 65 | begin 66 | CreateRouters; 67 | CreateMenuSuperior; 68 | end; 69 | 70 | procedure TPageCadastros.Props(aValue: TProps); 71 | begin 72 | if (aValue.PropString <> '') and (aValue.Key = 'TelaCadastro') then 73 | Label1.Text := aValue.PropString; 74 | 75 | aValue.Free; 76 | end; 77 | 78 | procedure TPageCadastros.CreateRouters; 79 | begin 80 | TRouter4D.Switch.Router('Clientes', TPagePrincipal, 'cadastros'); 81 | TRouter4D.Switch.Router('Fornecedores', TSubCadastros, 'cadastros'); 82 | TRouter4D.Switch.Router('Produtos', TSubCadastros, 'cadastros'); 83 | end; 84 | 85 | procedure TPageCadastros.CreateMenuSuperior; 86 | begin 87 | Layout4.AddObject( 88 | TComponentButton01.Create(Self) 89 | .createButton('Clientes') 90 | ); 91 | 92 | Layout4.AddObject( 93 | TComponentButton01.Create(Self) 94 | .createButton('Produtos') 95 | ); 96 | 97 | Layout4.AddObject( 98 | TComponentButton01.Create(Self) 99 | .createButton('Fornecedores') 100 | ); 101 | end; 102 | 103 | function TPageCadastros.Render: TFMXObject; 104 | begin 105 | Label1.Text := 'Cadastros'; 106 | Result := Layout1; 107 | end; 108 | 109 | procedure TPageCadastros.UnRender; 110 | begin 111 | // 112 | end; 113 | 114 | end. 115 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Page.Principal.fmx: -------------------------------------------------------------------------------- 1 | object PagePrincipal: TPagePrincipal 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form3' 5 | ClientHeight = 480 6 | ClientWidth = 640 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | DesignerMasterStyle = 0 11 | object Layout1: TLayout 12 | Align = Client 13 | Size.Width = 640.000000000000000000 14 | Size.Height = 480.000000000000000000 15 | Size.PlatformDefault = False 16 | object Label1: TLabel 17 | Align = Client 18 | StyledSettings = [Family, Style, FontColor] 19 | Size.Width = 640.000000000000000000 20 | Size.Height = 480.000000000000000000 21 | Size.PlatformDefault = False 22 | TextSettings.Font.Size = 30.000000000000000000 23 | TextSettings.HorzAlign = Center 24 | Text = 'Home' 25 | end 26 | object Button1: TButton 27 | Anchors = [] 28 | Position.X = 240.000000000000000000 29 | Position.Y = 264.000000000000000000 30 | Size.Width = 169.000000000000000000 31 | Size.Height = 33.000000000000000000 32 | Size.PlatformDefault = False 33 | Text = 'Cadastros Simples' 34 | OnClick = Button1Click 35 | end 36 | object Button2: TButton 37 | Anchors = [] 38 | Position.X = 240.000000000000000000 39 | Position.Y = 304.000000000000000000 40 | Size.Width = 169.000000000000000000 41 | Size.Height = 33.000000000000000000 42 | Size.PlatformDefault = False 43 | Text = 'Cadastros com Props' 44 | OnClick = Button2Click 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Page.Principal.pas: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academiadocodigo/Router4Delphi/eedd012e4b68cbda69067f9baf3f9ccfb628abc6/Samples/FMX/SimpleDemo/SimpleDemo.View.Page.Principal.pas -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Principal.fmx: -------------------------------------------------------------------------------- 1 | object Form2: TForm2 2 | Left = 0 3 | Top = 0 4 | Caption = 'Form2' 5 | ClientHeight = 586 6 | ClientWidth = 875 7 | FormFactor.Width = 320 8 | FormFactor.Height = 480 9 | FormFactor.Devices = [Desktop] 10 | OnShow = FormShow 11 | DesignerMasterStyle = 0 12 | object Layout1: TLayout 13 | Align = Client 14 | Size.Width = 875.000000000000000000 15 | Size.Height = 586.000000000000000000 16 | Size.PlatformDefault = False 17 | object Layout2: TLayout 18 | Align = Top 19 | Size.Width = 875.000000000000000000 20 | Size.Height = 50.000000000000000000 21 | Size.PlatformDefault = False 22 | object Rectangle1: TRectangle 23 | Align = Contents 24 | Fill.Color = xFF36414A 25 | Size.Width = 875.000000000000000000 26 | Size.Height = 50.000000000000000000 27 | Size.PlatformDefault = False 28 | Stroke.Kind = None 29 | end 30 | object Label1: TLabel 31 | Align = Contents 32 | StyledSettings = [Family, Style] 33 | Margins.Right = 10.000000000000000000 34 | Size.Width = 865.000000000000000000 35 | Size.Height = 50.000000000000000000 36 | Size.PlatformDefault = False 37 | TextSettings.Font.Size = 15.000000000000000000 38 | TextSettings.FontColor = claWhite 39 | TextSettings.HorzAlign = Trailing 40 | Text = 'Router4D - SimpleDemo' 41 | end 42 | end 43 | object Layout4: TLayout 44 | Align = Client 45 | Size.Width = 705.000000000000000000 46 | Size.Height = 536.000000000000000000 47 | Size.PlatformDefault = False 48 | end 49 | object Layout3: TLayout 50 | Align = Left 51 | Position.Y = 50.000000000000000000 52 | Size.Width = 170.000000000000000000 53 | Size.Height = 536.000000000000000000 54 | Size.PlatformDefault = False 55 | object Rectangle2: TRectangle 56 | Align = Contents 57 | Fill.Color = xFF2D2F32 58 | Size.Width = 170.000000000000000000 59 | Size.Height = 536.000000000000000000 60 | Size.PlatformDefault = False 61 | Stroke.Kind = None 62 | end 63 | object Layout5: TLayout 64 | Align = Client 65 | Size.Width = 170.000000000000000000 66 | Size.Height = 536.000000000000000000 67 | Size.PlatformDefault = False 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.View.Principal.pas: -------------------------------------------------------------------------------- 1 | unit SimpleDemo.View.Principal; 2 | 3 | interface 4 | 5 | uses 6 | System.SysUtils, 7 | System.Types, 8 | System.UITypes, 9 | System.Classes, 10 | System.Variants, 11 | FMX.Types, 12 | FMX.Controls, 13 | FMX.Forms, 14 | FMX.Graphics, 15 | FMX.Dialogs, 16 | FMX.Controls.Presentation, 17 | FMX.StdCtrls, 18 | FMX.ListBox, 19 | FMX.Layouts, 20 | FMX.Objects, FMX.Edit, FMX.SearchBox, FMX.MultiView; 21 | 22 | type 23 | TForm2 = class(TForm) 24 | Layout1: TLayout; 25 | Layout2: TLayout; 26 | Layout3: TLayout; 27 | Layout4: TLayout; 28 | Rectangle1: TRectangle; 29 | Rectangle2: TRectangle; 30 | Label1: TLabel; 31 | Layout5: TLayout; 32 | procedure FormShow(Sender: TObject); 33 | private 34 | procedure RegisterRouters; 35 | procedure createSideBar; 36 | { Private declarations } 37 | public 38 | { Public declarations } 39 | end; 40 | 41 | var 42 | Form2: TForm2; 43 | 44 | implementation 45 | 46 | uses 47 | Router4D, 48 | SimpleDemo.View.Page.Cadastros, 49 | SimpleDemo.View.Page.Principal; 50 | 51 | {$R *.fmx} 52 | 53 | procedure TForm2.FormShow(Sender: TObject); 54 | begin 55 | RegisterRouters; 56 | TRouter4D.Render.SetElement(Layout4, Layout1); 57 | end; 58 | 59 | procedure TForm2.RegisterRouters; 60 | begin 61 | TRouter4D.Switch.Router('Inicio', TPagePrincipal); 62 | TRouter4D.Switch.Router('Cadastros', TPageCadastros); 63 | TRouter4D.Switch.Router('Configuracoes', TPageCadastros); 64 | createSideBar; 65 | end; 66 | 67 | procedure TForm2.createSideBar; 68 | begin 69 | TRouter4D 70 | .SideBar 71 | .MainContainer(Layout5) 72 | .LinkContainer(Layout4) 73 | .FontSize(15) 74 | .FontColor(4294967295) 75 | .ItemHeigth(60) 76 | .RenderToListBox; 77 | end; 78 | 79 | end. 80 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.dpr: -------------------------------------------------------------------------------- 1 | program SimpleDemo; 2 | 3 | uses 4 | System.StartUpCopy, 5 | FMX.Forms, 6 | SimpleDemo.View.Principal in 'SimpleDemo.View.Principal.pas' {Form2}, 7 | SimpleDemo.View.Page.Principal in 'SimpleDemo.View.Page.Principal.pas' {PagePrincipal}, 8 | SimpleDemo.View.Page.Cadastros in 'SimpleDemo.View.Page.Cadastros.pas' {PageCadastros}, 9 | SimpleDemo.View.Page.Cadastros.Sub in 'SimpleDemo.View.Page.Cadastros.Sub.pas' {SubCadastros}, 10 | SimpleDemo.View.Components.Button01 in 'SimpleDemo.View.Components.Button01.pas' {ComponentButton01}; 11 | 12 | {$R *.res} 13 | 14 | begin 15 | ReportMemoryLeaksOnShutdown := True; 16 | Application.Initialize; 17 | Application.CreateForm(TForm2, Form2); 18 | Application.Run; 19 | end. 20 | -------------------------------------------------------------------------------- /Samples/FMX/SimpleDemo/SimpleDemo.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academiadocodigo/Router4Delphi/eedd012e4b68cbda69067f9baf3f9ccfb628abc6/Samples/FMX/SimpleDemo/SimpleDemo.res -------------------------------------------------------------------------------- /Samples/VCL/Main.dfm: -------------------------------------------------------------------------------- 1 | object fMain: TfMain 2 | Left = 0 3 | Top = 0 4 | Caption = 'fMain' 5 | ClientHeight = 678 6 | ClientWidth = 1178 7 | Color = clBtnFace 8 | Font.Charset = DEFAULT_CHARSET 9 | Font.Color = clWindowText 10 | Font.Height = -11 11 | Font.Name = 'Tahoma' 12 | Font.Style = [] 13 | OldCreateOrder = False 14 | OnCreate = FormCreate 15 | PixelsPerInch = 96 16 | TextHeight = 13 17 | object pnlBackground: TPanel 18 | Left = 0 19 | Top = 0 20 | Width = 1178 21 | Height = 678 22 | Align = alClient 23 | TabOrder = 0 24 | object pnlMain: TPanel 25 | Left = 1 26 | Top = 1 27 | Width = 192 28 | Height = 676 29 | Align = alLeft 30 | TabOrder = 0 31 | end 32 | object pnlEmbed: TPanel 33 | Left = 193 34 | Top = 1 35 | Width = 984 36 | Height = 676 37 | Align = alClient 38 | TabOrder = 1 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /Samples/VCL/Main.pas: -------------------------------------------------------------------------------- 1 | unit Main; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, 7 | Winapi.Messages, 8 | System.SysUtils, 9 | System.Variants, 10 | System.Classes, 11 | Vcl.Graphics, 12 | Vcl.Controls, 13 | Vcl.Forms, 14 | Vcl.Dialogs, 15 | Vcl.ExtCtrls, 16 | Router4D, 17 | View.Page.Main.Cadastro, 18 | Vcl.StdCtrls; 19 | 20 | type 21 | TfMain = class(TForm) 22 | pnlBackground: TPanel; 23 | pnlMain: TPanel; 24 | pnlEmbed: TPanel; 25 | procedure FormCreate(Sender: TObject); 26 | private 27 | procedure RegisterRouters; 28 | public 29 | { Public declarations } 30 | end; 31 | 32 | var 33 | fMain: TfMain; 34 | 35 | implementation 36 | 37 | {$R *.dfm} 38 | 39 | uses 40 | View.Page.Customer, 41 | View.Page.Product; 42 | 43 | procedure TfMain.FormCreate(Sender: TObject); 44 | begin 45 | RegisterRouters; 46 | TRouter4D.Render.SetElement(pnlEmbed, pnlBackground); 47 | end; 48 | 49 | procedure TfMain.RegisterRouters; 50 | begin 51 | TRouter4D.Switch.Router('Start', TfViewPageMainCadastro); 52 | TRouter4D.Switch.Router('Product', TfViewPageProduct); 53 | TRouter4D.Switch.Router('Customer', TfViewPageCustomer); 54 | end; 55 | 56 | end. 57 | 58 | -------------------------------------------------------------------------------- /Samples/VCL/Router.dpr: -------------------------------------------------------------------------------- 1 | program Router; 2 | 3 | uses 4 | Vcl.Forms, 5 | Main in 'Main.pas' {fMain}, 6 | View.Page.Main.Cadastro in 'pages\View.Page.Main.Cadastro.pas' {fViewPageMainCadastro}, 7 | View.Page.Template in 'pages\View.Page.Template.pas' {fViewPageTemplate}, 8 | View.Page.Product in 'pages\View.Page.Product.pas' {fViewPageProduct}, 9 | View.Page.Customer in 'pages\View.Page.Customer.pas' {fViewPageCustomer}; 10 | 11 | {$R *.res} 12 | 13 | begin 14 | ReportMemoryLeaksOnShutdown := True; 15 | Application.Initialize; 16 | Application.MainFormOnTaskbar := True; 17 | Application.CreateForm(TfMain, fMain); 18 | Application.Run; 19 | end. 20 | -------------------------------------------------------------------------------- /Samples/VCL/pages/View.Page.Customer.dfm: -------------------------------------------------------------------------------- 1 | inherited fViewPageCustomer: TfViewPageCustomer 2 | Caption = 'fViewPageCustomer' 3 | PixelsPerInch = 96 4 | TextHeight = 13 5 | inherited pnlBackground: TPanel 6 | ExplicitWidth = 715 7 | ExplicitHeight = 463 8 | inherited lblTitle: TLabel 9 | Width = 242 10 | Caption = 'Cadastro de Clientes' 11 | ExplicitWidth = 242 12 | end 13 | inherited btnBack: TButton 14 | TabOrder = 1 15 | ExplicitTop = 415 16 | end 17 | object edt1: TEdit 18 | Left = 16 19 | Top = 120 20 | Width = 121 21 | Height = 21 22 | TabOrder = 0 23 | Text = 'edt1' 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /Samples/VCL/pages/View.Page.Customer.pas: -------------------------------------------------------------------------------- 1 | unit View.Page.Customer; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, 7 | Winapi.Messages, 8 | System.SysUtils, 9 | System.Variants, 10 | System.Classes, 11 | Vcl.Graphics, 12 | Vcl.Controls, 13 | Vcl.Forms, 14 | Vcl.Dialogs, 15 | View.Page.Template, 16 | Vcl.ExtCtrls, 17 | Vcl.StdCtrls; 18 | 19 | type 20 | TfViewPageCustomer = class(TfViewPageTemplate) 21 | edt1: TEdit; 22 | private 23 | { Private declarations } 24 | public 25 | { Public declarations } 26 | end; 27 | 28 | var 29 | fViewPageCustomer: TfViewPageCustomer; 30 | 31 | implementation 32 | 33 | {$R *.dfm} 34 | 35 | end. 36 | 37 | -------------------------------------------------------------------------------- /Samples/VCL/pages/View.Page.Main.Cadastro.dfm: -------------------------------------------------------------------------------- 1 | object fViewPageMainCadastro: TfViewPageMainCadastro 2 | Left = 0 3 | Top = 0 4 | Align = alClient 5 | BorderStyle = bsNone 6 | Caption = 'fViewPageMainCadastro' 7 | ClientHeight = 484 8 | ClientWidth = 799 9 | Color = clBtnFace 10 | Font.Charset = DEFAULT_CHARSET 11 | Font.Color = clWindowText 12 | Font.Height = -11 13 | Font.Name = 'Tahoma' 14 | Font.Style = [] 15 | OldCreateOrder = False 16 | PixelsPerInch = 96 17 | TextHeight = 13 18 | object pnlAll: TPanel 19 | Left = 0 20 | Top = 0 21 | Width = 799 22 | Height = 484 23 | Align = alClient 24 | TabOrder = 0 25 | object btnProduct: TButton 26 | Left = 24 27 | Top = 32 28 | Width = 153 29 | Height = 25 30 | Caption = 'Product' 31 | TabOrder = 0 32 | OnClick = btnProductClick 33 | end 34 | object btnProductProp: TButton 35 | Left = 24 36 | Top = 63 37 | Width = 153 38 | Height = 25 39 | Caption = 'Product With Prop' 40 | TabOrder = 1 41 | OnClick = btnProductPropClick 42 | end 43 | object btnCustomer: TButton 44 | Left = 24 45 | Top = 104 46 | Width = 153 47 | Height = 25 48 | Caption = 'Customer' 49 | TabOrder = 2 50 | OnClick = btnCustomerClick 51 | end 52 | object btnCustomerWithProps: TButton 53 | Left = 24 54 | Top = 135 55 | Width = 153 56 | Height = 25 57 | Caption = 'Customer With Prop' 58 | TabOrder = 3 59 | OnClick = btnCustomerWithPropsClick 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /Samples/VCL/pages/View.Page.Main.Cadastro.pas: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academiadocodigo/Router4Delphi/eedd012e4b68cbda69067f9baf3f9ccfb628abc6/Samples/VCL/pages/View.Page.Main.Cadastro.pas -------------------------------------------------------------------------------- /Samples/VCL/pages/View.Page.Product.dfm: -------------------------------------------------------------------------------- 1 | inherited fViewPageProduct: TfViewPageProduct 2 | Caption = 'fViewPageProduct' 3 | PixelsPerInch = 96 4 | TextHeight = 13 5 | inherited pnlBackground: TPanel 6 | ExplicitWidth = 715 7 | ExplicitHeight = 463 8 | inherited btnBack: TButton 9 | ExplicitTop = 415 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Samples/VCL/pages/View.Page.Product.pas: -------------------------------------------------------------------------------- 1 | unit View.Page.Product; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, 7 | Winapi.Messages, 8 | System.SysUtils, 9 | System.Variants, 10 | System.Classes, 11 | Vcl.Graphics, 12 | Vcl.Controls, 13 | Vcl.Forms, 14 | Vcl.Dialogs, 15 | View.Page.Template, 16 | Vcl.ExtCtrls, 17 | Vcl.StdCtrls; 18 | 19 | type 20 | TfViewPageProduct = class(TfViewPageTemplate) 21 | private 22 | { Private declarations } 23 | public 24 | { Public declarations } 25 | end; 26 | 27 | var 28 | fViewPageProduct: TfViewPageProduct; 29 | 30 | implementation 31 | 32 | {$R *.dfm} 33 | 34 | end. 35 | 36 | -------------------------------------------------------------------------------- /Samples/VCL/pages/View.Page.Template.dfm: -------------------------------------------------------------------------------- 1 | object fViewPageTemplate: TfViewPageTemplate 2 | Left = 0 3 | Top = 0 4 | Align = alClient 5 | BorderStyle = bsNone 6 | Caption = 'fViewPageTemplate' 7 | ClientHeight = 463 8 | ClientWidth = 715 9 | Color = clBtnFace 10 | Font.Charset = DEFAULT_CHARSET 11 | Font.Color = clWindowText 12 | Font.Height = -11 13 | Font.Name = 'Tahoma' 14 | Font.Style = [] 15 | OldCreateOrder = False 16 | PixelsPerInch = 96 17 | TextHeight = 13 18 | object pnlBackground: TPanel 19 | Left = 0 20 | Top = 0 21 | Width = 715 22 | Height = 463 23 | Align = alClient 24 | TabOrder = 0 25 | DesignSize = ( 26 | 715 27 | 463) 28 | object lblTitle: TLabel 29 | Left = 16 30 | Top = 24 31 | Width = 142 32 | Height = 33 33 | Caption = 'Cadastro de' 34 | Font.Charset = DEFAULT_CHARSET 35 | Font.Color = clWindowText 36 | Font.Height = -27 37 | Font.Name = 'Tahoma' 38 | Font.Style = [] 39 | ParentFont = False 40 | end 41 | object lblSubtitle: TLabel 42 | Left = 16 43 | Top = 67 44 | Width = 70 45 | Height = 19 46 | Caption = 'lblSubtitle' 47 | Font.Charset = DEFAULT_CHARSET 48 | Font.Color = clWindowText 49 | Font.Height = -16 50 | Font.Name = 'Tahoma' 51 | Font.Style = [] 52 | ParentFont = False 53 | end 54 | object btnBack: TButton 55 | Left = 16 56 | Top = 415 57 | Width = 75 58 | Height = 25 59 | Anchors = [akLeft, akBottom] 60 | Caption = 'Voltar' 61 | TabOrder = 0 62 | OnClick = btnBackClick 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /Samples/VCL/pages/View.Page.Template.pas: -------------------------------------------------------------------------------- 1 | unit View.Page.Template; 2 | 3 | interface 4 | 5 | uses 6 | Winapi.Windows, 7 | Winapi.Messages, 8 | System.SysUtils, 9 | System.Variants, 10 | System.Classes, 11 | Vcl.Graphics, 12 | Vcl.Controls, 13 | Vcl.Forms, 14 | Vcl.Dialogs, 15 | Router4D.Interfaces, 16 | Vcl.ExtCtrls, 17 | Vcl.StdCtrls, 18 | Router4D.Props, 19 | Router4D; 20 | 21 | type 22 | TfViewPageTemplate = class(TForm, IRouter4DComponent) 23 | pnlBackground: TPanel; 24 | btnBack: TButton; 25 | lblTitle: TLabel; 26 | lblSubtitle: TLabel; 27 | procedure btnBackClick(Sender: TObject); 28 | private 29 | function Render: TForm; 30 | procedure UnRender; 31 | public 32 | [Subscribe] 33 | procedure Props(AValue: TProps); 34 | end; 35 | 36 | var 37 | fViewPageTemplate: TfViewPageTemplate; 38 | 39 | implementation 40 | 41 | {$R *.dfm} 42 | 43 | { TfViewPageTemplate } 44 | 45 | procedure TfViewPageTemplate.btnBackClick(Sender: TObject); 46 | begin 47 | TRouter4D.Link.&To('Start'); 48 | end; 49 | 50 | procedure TfViewPageTemplate.Props(AValue: TProps); 51 | begin 52 | lblSubtitle.Caption := AValue.PropString; 53 | 54 | AValue.Free; 55 | end; 56 | 57 | function TfViewPageTemplate.Render: TForm; 58 | begin 59 | Result := Self; 60 | end; 61 | 62 | procedure TfViewPageTemplate.UnRender; 63 | begin 64 | 65 | end; 66 | 67 | end. 68 | 69 | -------------------------------------------------------------------------------- /assets/logo.fw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/academiadocodigo/Router4Delphi/eedd012e4b68cbda69067f9baf3f9ccfb628abc6/assets/logo.fw.png -------------------------------------------------------------------------------- /boss-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "d41d8cd98f00b204e9800998ecf8427e", 3 | "updated": "2021-04-29T17:11:41.0855564-03:00", 4 | "installedModules": {} 5 | } -------------------------------------------------------------------------------- /boss.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Router4Delphi", 3 | "description": "", 4 | "version": "1.0.0", 5 | "homepage": "", 6 | "mainsrc": "src", 7 | "projects": [], 8 | "dependencies": {} 9 | } -------------------------------------------------------------------------------- /src/DuckListU.pas: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // 3 | // Delphi MVC Framework 4 | // 5 | // Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team 6 | // 7 | // https://github.com/danieleteti/delphimvcframework 8 | // 9 | // *************************************************************************** 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | // 23 | // *************************************************************************** 24 | 25 | unit DuckListU; 26 | 27 | interface 28 | 29 | uses 30 | RTTI, 31 | Classes, 32 | // superobject, 33 | Generics.Collections, 34 | SysUtils, 35 | TypInfo; 36 | 37 | type 38 | TDuckTypedList=class; 39 | 40 | TdormObjectStatus=(osDirty=0, osClean, osUnknown, osDeleted); 41 | 42 | EdormException=class(Exception) 43 | 44 | end; 45 | 46 | EdormValidationException=class(EdormException) 47 | 48 | end; 49 | 50 | TdormEnvironment=(deDevelopment, deTest, deRelease); 51 | TdormObjectOwner=(ooItself, ooParent); 52 | TdormSaveType=(stAllGraph, stSingleObject); 53 | TdormRelations=set of (drBelongsTo, drHasMany, drHasOne); 54 | TdormFillOptions=set of (CallAfterLoadEvent); 55 | 56 | IList=interface 57 | ['{2A1BCB3C-17A2-4F8D-B6FB-32B2A1BFE840}'] 58 | function Add(const Value: TObject): Integer; 59 | procedure Clear; 60 | function Count: Integer; 61 | function GetItem(index: Integer): TObject; 62 | end; 63 | 64 | TdormListEnumerator=class(TEnumerator) 65 | protected 66 | FPosition: Int64; 67 | FDuckTypedList: TDuckTypedList; 68 | 69 | protected 70 | function DoGetCurrent: TObject; override; 71 | function DoMoveNext: boolean; override; 72 | 73 | public 74 | constructor Create(ADuckTypedList: TDuckTypedList); 75 | end; 76 | 77 | TSortingType=(soAscending, soDescending); 78 | 79 | IWrappedList=interface 80 | ['{B60AF5A6-7C31-4EAA-8DFB-D8BD3E112EE7}'] 81 | function Count: Integer; 82 | function GetItem(const index: Integer): TObject; 83 | procedure Add(const AObject: TObject); 84 | procedure Clear; 85 | function GetEnumerator: TdormListEnumerator; 86 | function WrappedObject: TObject; 87 | procedure Sort(const PropertyName: string; Order: TSortingType=soAscending); 88 | function GetOwnsObjects: boolean; 89 | procedure SetOwnsObjects(const Value: boolean); 90 | property OwnsObjects: boolean read GetOwnsObjects write SetOwnsObjects; 91 | end; 92 | 93 | TDuckTypedList=class(TInterfacedObject, IWrappedList) 94 | protected 95 | FCTX: TRTTIContext; 96 | FObjectAsDuck: TObject; 97 | FAddMethod: TRttiMethod; 98 | FClearMethod: TRttiMethod; 99 | FCountProperty: TRttiProperty; 100 | FGetItemMethod: TRttiMethod; 101 | FGetCountMethod: TRttiMethod; 102 | function Count: Integer; 103 | function GetItem(const index: Integer): TObject; 104 | procedure Add(const AObject: TObject); 105 | procedure Clear; 106 | function WrappedObject: TObject; 107 | procedure QuickSort(List: IWrappedList; L, R: Integer; SCompare: TFunc); overload; 108 | 109 | procedure QuickSort(List: IWrappedList; SCompare: TFunc); overload; 110 | procedure Sort(const PropertyName: string; Order: TSortingType=soAscending); 111 | 112 | public 113 | constructor Create(AObjectAsDuck: TObject); 114 | destructor Destroy; override; 115 | function GetEnumerator: TdormListEnumerator; 116 | function GetOwnsObjects: boolean; 117 | procedure SetOwnsObjects(const Value: boolean); 118 | property OwnsObjects: boolean read GetOwnsObjects write SetOwnsObjects; 119 | class function CanBeWrappedAsList(const AObjectAsDuck: TObject): boolean; 120 | end; 121 | 122 | function WrapAsList(const AObject: TObject): IWrappedList; 123 | 124 | implementation 125 | 126 | uses System.Math, 127 | RTTIUtilsU; 128 | 129 | constructor TdormListEnumerator.Create(ADuckTypedList: TDuckTypedList); 130 | begin 131 | inherited Create; 132 | FDuckTypedList := ADuckTypedList; 133 | FPosition := -1; 134 | end; 135 | 136 | function TdormListEnumerator.DoGetCurrent: TObject; 137 | begin 138 | if FPosition>-1 then 139 | Result := FDuckTypedList.GetItem(FPosition) 140 | else 141 | raise Exception.Create('Enumerator error: Call MoveNext first'); 142 | end; 143 | 144 | function TdormListEnumerator.DoMoveNext: boolean; 145 | begin 146 | if FPositionnil)and(FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('Clear')<>nil) 170 | 171 | {$IF CompilerVersion >= 23} 172 | and(FCTX.GetType(AObjectAsDuck.ClassInfo).GetIndexedProperty('Items').ReadMethod<>nil) 173 | 174 | {$IFEND} 175 | and((FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('GetItem')<>nil)or(FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('GetElement')<> 176 | nil))and(FCTX.GetType(AObjectAsDuck.ClassInfo).GetProperty('Count')<>nil) 177 | 178 | end; 179 | 180 | procedure TDuckTypedList.Clear; 181 | begin 182 | FClearMethod.Invoke(FObjectAsDuck, []); 183 | end; 184 | 185 | function TDuckTypedList.Count: Integer; 186 | begin 187 | if Assigned(FCountProperty) then 188 | Result := FCountProperty.GetValue(FObjectAsDuck).AsInteger 189 | else 190 | Result := FGetCountMethod.Invoke(FObjectAsDuck, []).AsInteger; 191 | 192 | end; 193 | 194 | constructor TDuckTypedList.Create(AObjectAsDuck: TObject); 195 | begin 196 | inherited Create; 197 | FObjectAsDuck := AObjectAsDuck; 198 | FAddMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('Add'); 199 | if not Assigned(FAddMethod) then 200 | raise EdormException.Create('Cannot find method "Add" in the duck object'); 201 | FClearMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('Clear'); 202 | if not Assigned(FClearMethod) then 203 | raise EdormException.Create('Cannot find method "Clear" in the duck object'); 204 | FGetItemMethod := nil; 205 | 206 | {$IF CompilerVersion >= 23} 207 | FGetItemMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetIndexedProperty('Items').ReadMethod; 208 | 209 | {$IFEND} 210 | if not Assigned(FGetItemMethod) then 211 | FGetItemMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('GetItem'); 212 | if not Assigned(FGetItemMethod) then 213 | FGetItemMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('GetElement'); 214 | if not Assigned(FGetItemMethod) then 215 | raise EdormException.Create 216 | ('Cannot find method Indexed property "Items" or method "GetItem" or method "GetElement" in the duck object'); 217 | FCountProperty := FCTX.GetType(AObjectAsDuck.ClassInfo).GetProperty('Count'); 218 | if not Assigned(FCountProperty) then 219 | begin 220 | FGetCountMethod := FCTX.GetType(AObjectAsDuck.ClassInfo).GetMethod('Count'); 221 | if not Assigned(FGetCountMethod) then 222 | 223 | raise EdormException.Create('Cannot find property/method "Count" in the duck object'); 224 | end; 225 | end; 226 | 227 | destructor TDuckTypedList.Destroy; 228 | begin 229 | 230 | inherited; 231 | end; 232 | 233 | function TDuckTypedList.GetItem(const index: Integer): TObject; 234 | begin 235 | Result := FGetItemMethod.Invoke(FObjectAsDuck, [index]).AsObject; 236 | end; 237 | 238 | function TDuckTypedList.GetOwnsObjects: boolean; 239 | begin 240 | Result := TRTTIUtils.GetProperty(FObjectAsDuck, 'OwnsObjects').AsBoolean 241 | end; 242 | 243 | function TDuckTypedList.WrappedObject: TObject; 244 | begin 245 | Result := FObjectAsDuck; 246 | end; 247 | 248 | function WrapAsList(const AObject: TObject): IWrappedList; 249 | begin 250 | try 251 | Result := TDuckTypedList.Create(AObject); 252 | except 253 | Result := nil; 254 | end; 255 | end; 256 | 257 | procedure TDuckTypedList.QuickSort(List: IWrappedList; L, R: Integer; SCompare: TFunc); 258 | var 259 | I, J: Integer; 260 | p: TObject; 261 | begin 262 | { 07/08/2013: This method is based on QuickSort procedure from 263 | Classes.pas, (c) Borland Software Corp. 264 | but modified to be part of TDuckListU unit. It implements the 265 | standard quicksort algorithm, 266 | delegating comparison operation to an anonimous. 267 | The Borland version delegates to a pure function 268 | pointer, which is problematic in some cases. } 269 | repeat 270 | I := L; 271 | J := R; 272 | p := List.GetItem((L+R) shr 1); 273 | repeat 274 | while SCompare(TObject(List.GetItem(I)), p)<0 do 275 | Inc(I); 276 | while SCompare(TObject(List.GetItem(J)), p)>0 do 277 | Dec(J); 278 | if I<=J then 279 | begin 280 | TRTTIUtils.MethodCall(List.WrappedObject, 'Exchange', [I, J]); 281 | Inc(I); 282 | Dec(J); 283 | end; 284 | until I>J; 285 | if L=R; 289 | end; 290 | 291 | procedure TDuckTypedList.QuickSort(List: IWrappedList; SCompare: TFunc); 292 | begin 293 | QuickSort(List, 0, List.Count-1, SCompare); 294 | end; 295 | 296 | function CompareValue(const Left, Right: TValue): Integer; 297 | begin 298 | if Left.IsOrdinal then 299 | begin 300 | Result := System.Math.CompareValue(Left.AsOrdinal, Right.AsOrdinal); 301 | end 302 | else if Left.Kind=tkFloat then 303 | begin 304 | Result := System.Math.CompareValue(Left.AsExtended, Right.AsExtended); 305 | end 306 | else if Left.Kind in [tkString, tkUString, tkWString, tkLString] then 307 | begin 308 | Result := CompareText(Left.AsString, Right.AsString); 309 | end 310 | else 311 | begin 312 | Result := 0; 313 | end; 314 | end; 315 | 316 | procedure TDuckTypedList.SetOwnsObjects(const Value: boolean); 317 | begin 318 | TRTTIUtils.SetProperty(FObjectAsDuck, 'OwnsObjects', Value); 319 | end; 320 | 321 | procedure TDuckTypedList.Sort(const PropertyName: string; Order: TSortingType); 322 | begin 323 | if Order=soAscending then 324 | QuickSort(self, 325 | function(Left, Right: TObject): Integer 326 | begin 327 | Result := CompareValue(TRTTIUtils.GetProperty(Left, PropertyName), TRTTIUtils.GetProperty(Right, PropertyName)); 328 | end) 329 | else 330 | QuickSort(self, 331 | function(Left, Right: TObject): Integer 332 | begin 333 | Result := -1*CompareValue(TRTTIUtils.GetProperty(Left, PropertyName), TRTTIUtils.GetProperty(Right, PropertyName)); 334 | end); 335 | end; 336 | 337 | end. 338 | -------------------------------------------------------------------------------- /src/EventBus.Core.pas: -------------------------------------------------------------------------------- 1 | { ******************************************************************************* 2 | Copyright 2016-2019 Daniele Spinetti 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | ******************************************************************************** } 16 | 17 | unit EventBus.Core; 18 | 19 | interface 20 | 21 | uses 22 | System.SyncObjs, EventBus.Subscribers, Generics.Collections, 23 | System.SysUtils, System.Classes, Router4D.Props; 24 | 25 | type 26 | 27 | TEventBus = class(TInterfacedObject, IEventBus) 28 | var 29 | FTypesOfGivenSubscriber: TObjectDictionary>; 30 | FSubscriptionsOfGivenEventType 31 | : TObjectDictionary>; 32 | FCustomClonerDict: TDictionary; 33 | FOnCloneEvent: TCloneEventCallback; 34 | procedure Subscribe(ASubscriber: TObject; 35 | ASubscriberMethod: TSubscriberMethod); 36 | procedure UnsubscribeByEventType(ASubscriber: TObject; AEventType: TClass); 37 | procedure InvokeSubscriber(ASubscription: TSubscription; AEvent: TObject); 38 | function GenerateTProc(ASubscription: TSubscription; 39 | AEvent: TObject): TProc; 40 | function GenerateThreadProc(ASubscription: TSubscription; AEvent: TObject) 41 | : TThreadProcedure; 42 | protected 43 | procedure SetOnCloneEvent(const aCloneEvent: TCloneEventCallback); 44 | function CloneEvent(AEvent: TObject): TObject; virtual; 45 | procedure PostToSubscription(ASubscription: TSubscription; AEvent: TObject; 46 | AIsMainThread: Boolean); virtual; 47 | public 48 | constructor Create; virtual; 49 | destructor Destroy; override; 50 | procedure RegisterSubscriber(ASubscriber: TObject); virtual; 51 | function IsRegistered(ASubscriber: TObject): Boolean; 52 | procedure Unregister(ASubscriber: TObject); virtual; 53 | procedure Post(AEvent: TObject; const AContext: String = ''; 54 | AEventOwner: Boolean = true); virtual; 55 | property TypesOfGivenSubscriber: TObjectDictionary < TObject, 56 | TList < TClass >> read FTypesOfGivenSubscriber; 57 | property SubscriptionsOfGivenEventType: TObjectDictionary < TClass, 58 | TObjectList < TSubscription >> read FSubscriptionsOfGivenEventType; 59 | property OnCloneEvent: TCloneEventCallback write SetOnCloneEvent; 60 | procedure AddCustomClassCloning(const AQualifiedClassName: String; 61 | const aCloneEvent: TCloneEventMethod); 62 | procedure RemoveCustomClassCloning(const AQualifiedClassName: String); 63 | end; 64 | 65 | implementation 66 | 67 | uses 68 | System.Rtti, 69 | {$IF CompilerVersion >= 28.0} 70 | System.Threading, 71 | {$ENDIF} 72 | RTTIUtilsU; 73 | 74 | var 75 | FMREWSync: TMultiReadExclusiveWriteSynchronizer; 76 | 77 | { TEventBus } 78 | 79 | constructor TEventBus.Create; 80 | begin 81 | inherited Create; 82 | FSubscriptionsOfGivenEventType := TObjectDictionary < TClass, 83 | TObjectList < TSubscription >>.Create([doOwnsValues]); 84 | FTypesOfGivenSubscriber := TObjectDictionary < TObject, 85 | TList < TClass >>.Create([doOwnsValues]); 86 | FCustomClonerDict := TDictionary.Create; 87 | end; 88 | 89 | destructor TEventBus.Destroy; 90 | begin 91 | FreeAndNil(FSubscriptionsOfGivenEventType); 92 | FreeAndNil(FTypesOfGivenSubscriber); 93 | FreeAndNil(FCustomClonerDict); 94 | inherited; 95 | end; 96 | 97 | procedure TEventBus.AddCustomClassCloning(const AQualifiedClassName: String; 98 | const aCloneEvent: TCloneEventMethod); 99 | begin 100 | FCustomClonerDict.Add(AQualifiedClassName, aCloneEvent); 101 | end; 102 | 103 | function TEventBus.CloneEvent(AEvent: TObject): TObject; 104 | var 105 | LCloneEvent: TCloneEventMethod; 106 | begin 107 | if FCustomClonerDict.TryGetValue(AEvent.QualifiedClassName, LCloneEvent) then 108 | Result := LCloneEvent(AEvent) 109 | else if Assigned(FOnCloneEvent) then 110 | Result := FOnCloneEvent(AEvent) 111 | else 112 | Result := TRTTIUtils.Clone(AEvent); 113 | end; 114 | 115 | function TEventBus.GenerateThreadProc(ASubscription: TSubscription; 116 | AEvent: TObject): TThreadProcedure; 117 | begin 118 | Result := procedure 119 | begin 120 | if ASubscription.Active then 121 | begin 122 | ASubscription.SubscriberMethod.Method.Invoke(ASubscription.Subscriber, 123 | [AEvent]); 124 | end; 125 | end; 126 | end; 127 | 128 | function TEventBus.GenerateTProc(ASubscription: TSubscription; 129 | AEvent: TObject): TProc; 130 | begin 131 | Result := procedure 132 | begin 133 | if ASubscription.Active then 134 | begin 135 | ASubscription.SubscriberMethod.Method.Invoke(ASubscription.Subscriber, 136 | [AEvent]); 137 | end; 138 | end; 139 | end; 140 | 141 | procedure TEventBus.InvokeSubscriber(ASubscription: TSubscription; 142 | AEvent: TObject); 143 | begin 144 | try 145 | ASubscription.SubscriberMethod.Method.Invoke(ASubscription.Subscriber, 146 | [AEvent]); 147 | except 148 | on E: Exception do 149 | begin 150 | raise Exception.CreateFmt 151 | ('Error invoking subscriber method. Subscriber class: %s. Event type: %s. Original exception: %s: %s', 152 | [ASubscription.Subscriber.ClassName, 153 | ASubscription.SubscriberMethod.EventType.ClassName, E.ClassName, 154 | E.Message]); 155 | end; 156 | end; 157 | end; 158 | 159 | function TEventBus.IsRegistered(ASubscriber: TObject): Boolean; 160 | begin 161 | FMREWSync.BeginRead; 162 | try 163 | Result := FTypesOfGivenSubscriber.ContainsKey(ASubscriber); 164 | finally 165 | FMREWSync.EndRead; 166 | end; 167 | end; 168 | 169 | procedure TEventBus.Post(AEvent: TObject; const AContext: String = ''; 170 | AEventOwner: Boolean = true); 171 | var 172 | LSubscriptions: TObjectList; 173 | LSubscription: TSubscription; 174 | LEvent: TObject; 175 | LIsMainThread: Boolean; 176 | begin 177 | FMREWSync.BeginRead; 178 | try 179 | try 180 | LIsMainThread := MainThreadID = TThread.CurrentThread.ThreadID; 181 | 182 | FSubscriptionsOfGivenEventType.TryGetValue(AEvent.ClassType, 183 | LSubscriptions); 184 | 185 | if (not Assigned(LSubscriptions)) then 186 | Exit; 187 | 188 | for LSubscription in LSubscriptions do 189 | begin 190 | 191 | if not LSubscription.Active then 192 | continue; 193 | 194 | if ((not AContext.IsEmpty) and (LSubscription.Context <> AContext)) then 195 | continue; 196 | 197 | LEvent := CloneEvent(AEvent); 198 | PostToSubscription(LSubscription, LEvent, LIsMainThread); 199 | end; 200 | finally 201 | if (AEventOwner and Assigned(AEvent)) then 202 | AEvent.Free; 203 | end; 204 | finally 205 | FMREWSync.EndRead; 206 | end; 207 | end; 208 | 209 | procedure TEventBus.PostToSubscription(ASubscription: TSubscription; 210 | AEvent: TObject; AIsMainThread: Boolean); 211 | begin 212 | 213 | if not Assigned(ASubscription.Subscriber) then 214 | Exit; 215 | 216 | case ASubscription.SubscriberMethod.ThreadMode of 217 | Posting: 218 | InvokeSubscriber(ASubscription, AEvent); 219 | Main: 220 | if (AIsMainThread) then 221 | InvokeSubscriber(ASubscription, AEvent) 222 | else 223 | TThread.Queue(nil, GenerateThreadProc(ASubscription, AEvent)); 224 | Background: 225 | if (AIsMainThread) then 226 | {$IF CompilerVersion >= 28.0} 227 | TTask.Run(GenerateTProc(ASubscription, AEvent)) 228 | {$ELSE} 229 | TThread.CreateAnonymousThread(GenerateTProc(ASubscription, 230 | AEvent)).Start 231 | {$ENDIF} 232 | else 233 | InvokeSubscriber(ASubscription, AEvent); 234 | Async: 235 | {$IF CompilerVersion >= 28.0} 236 | TTask.Run(GenerateTProc(ASubscription, AEvent)); 237 | {$ELSE} 238 | TThread.CreateAnonymousThread(GenerateTProc(ASubscription, AEvent)).Start; 239 | {$ENDIF} 240 | else 241 | raise Exception.Create('Unknown thread mode'); 242 | end; 243 | 244 | end; 245 | 246 | procedure TEventBus.RegisterSubscriber(ASubscriber: TObject); 247 | var 248 | LSubscriberClass: TClass; 249 | LSubscriberMethods: TArray; 250 | LSubscriberMethod: TSubscriberMethod; 251 | begin 252 | FMREWSync.BeginWrite; 253 | try 254 | LSubscriberClass := ASubscriber.ClassType; 255 | LSubscriberMethods := TSubscribersFinder.FindSubscriberMethods 256 | (LSubscriberClass, true); 257 | for LSubscriberMethod in LSubscriberMethods do 258 | Subscribe(ASubscriber, LSubscriberMethod); 259 | finally 260 | FMREWSync.EndWrite; 261 | end; 262 | end; 263 | 264 | procedure TEventBus.RemoveCustomClassCloning(const AQualifiedClassName: String); 265 | begin 266 | // No exception is thrown if the key is not in the dictionary 267 | FCustomClonerDict.Remove(AQualifiedClassName); 268 | end; 269 | 270 | procedure TEventBus.SetOnCloneEvent(const aCloneEvent: TCloneEventCallback); 271 | begin 272 | FOnCloneEvent := aCloneEvent; 273 | end; 274 | 275 | procedure TEventBus.Subscribe(ASubscriber: TObject; 276 | ASubscriberMethod: TSubscriberMethod); 277 | var 278 | LEventType: TClass; 279 | LNewSubscription: TSubscription; 280 | LSubscriptions: TObjectList; 281 | LSubscribedEvents: TList; 282 | begin 283 | LEventType := ASubscriberMethod.EventType; 284 | LNewSubscription := TSubscription.Create(ASubscriber, ASubscriberMethod); 285 | if (not FSubscriptionsOfGivenEventType.ContainsKey(LEventType)) then 286 | begin 287 | LSubscriptions := TObjectList.Create(); 288 | FSubscriptionsOfGivenEventType.Add(LEventType, LSubscriptions); 289 | end 290 | else 291 | begin 292 | LSubscriptions := FSubscriptionsOfGivenEventType.Items[LEventType]; 293 | if (LSubscriptions.Contains(LNewSubscription)) then 294 | raise Exception.CreateFmt('Subscriber %s already registered to event %s ', 295 | [ASubscriber.ClassName, LEventType.ClassName]); 296 | end; 297 | 298 | LSubscriptions.Add(LNewSubscription); 299 | 300 | if (not FTypesOfGivenSubscriber.TryGetValue(ASubscriber, LSubscribedEvents)) 301 | then 302 | begin 303 | LSubscribedEvents := TList.Create; 304 | FTypesOfGivenSubscriber.Add(ASubscriber, LSubscribedEvents); 305 | end; 306 | LSubscribedEvents.Add(LEventType); 307 | end; 308 | 309 | procedure TEventBus.Unregister(ASubscriber: TObject); 310 | var 311 | LSubscribedTypes: TList; 312 | LEventType: TClass; 313 | begin 314 | FMREWSync.BeginWrite; 315 | try 316 | if FTypesOfGivenSubscriber.TryGetValue(ASubscriber, LSubscribedTypes) then 317 | begin 318 | for LEventType in LSubscribedTypes do 319 | UnsubscribeByEventType(ASubscriber, LEventType); 320 | FTypesOfGivenSubscriber.Remove(ASubscriber); 321 | end; 322 | // else { 323 | // Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass()); 324 | // } 325 | finally 326 | FMREWSync.EndWrite; 327 | end; 328 | end; 329 | 330 | procedure TEventBus.UnsubscribeByEventType(ASubscriber: TObject; 331 | AEventType: TClass); 332 | var 333 | LSubscriptions: TObjectList; 334 | LSize, I: Integer; 335 | LSubscription: TSubscription; 336 | begin 337 | LSubscriptions := FSubscriptionsOfGivenEventType.Items[AEventType]; 338 | if (not Assigned(LSubscriptions)) or (LSubscriptions.Count < 1) then 339 | Exit; 340 | LSize := LSubscriptions.Count; 341 | for I := LSize - 1 downto 0 do 342 | begin 343 | LSubscription := LSubscriptions[I]; 344 | // Notes: In case the subscriber has been freed but it didn't unregister itself, calling 345 | // LSubscription.Subscriber.Equals() will cause Access Violation, so we use '=' instead. 346 | if LSubscription.Subscriber = ASubscriber then 347 | begin 348 | LSubscription.Active := false; 349 | LSubscriptions.Delete(I); 350 | end; 351 | end; 352 | end; 353 | 354 | initialization 355 | 356 | FMREWSync := TMultiReadExclusiveWriteSynchronizer.Create; 357 | 358 | finalization 359 | 360 | FMREWSync.Free; 361 | 362 | end. 363 | -------------------------------------------------------------------------------- /src/EventBus.Subscribers.pas: -------------------------------------------------------------------------------- 1 | { ******************************************************************************* 2 | Copyright 2016-2019 Daniele Spinetti 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | ******************************************************************************** } 16 | 17 | unit EventBus.Subscribers; 18 | 19 | interface 20 | 21 | uses 22 | System.RTTI, Router4D.Props; 23 | 24 | type 25 | 26 | TSubscriberMethod = class(TObject) 27 | private 28 | FEventType: TClass; 29 | FThreadMode: TThreadMode; 30 | FMethod: TRttiMethod; 31 | FContext: string; 32 | procedure SetEventType(const Value: TClass); 33 | procedure SetMethod(const Value: TRttiMethod); 34 | procedure SetThreadMode(const Value: TThreadMode); 35 | procedure SetContext(const Value: String); 36 | public 37 | constructor Create(ARttiMethod: TRttiMethod; AEventType: TClass; 38 | AThreadMode: TThreadMode; const AContext: String = ''; 39 | APriority: Integer = 1); 40 | destructor Destroy; override; 41 | property EventType: TClass read FEventType write SetEventType; 42 | property Method: TRttiMethod read FMethod write SetMethod; 43 | property ThreadMode: TThreadMode read FThreadMode write SetThreadMode; 44 | property Context: String read FContext write SetContext; 45 | function Equals(Obj: TObject): Boolean; override; 46 | end; 47 | 48 | TSubscription = class(TObject) 49 | private 50 | FSubscriberMethod: TSubscriberMethod; 51 | FSubscriber: TObject; 52 | FActive: Boolean; 53 | procedure SetActive(const Value: Boolean); 54 | function GetActive: Boolean; 55 | procedure SetSubscriberMethod(const Value: TSubscriberMethod); 56 | procedure SetSubscriber(const Value: TObject); 57 | function GetContext: String; 58 | public 59 | constructor Create(ASubscriber: TObject; 60 | ASubscriberMethod: TSubscriberMethod); 61 | destructor Destroy; override; 62 | property Active: Boolean read GetActive write SetActive; 63 | property Subscriber: TObject read FSubscriber write SetSubscriber; 64 | property SubscriberMethod: TSubscriberMethod read FSubscriberMethod 65 | write SetSubscriberMethod; 66 | property Context: String read GetContext; 67 | function Equals(Obj: TObject): Boolean; override; 68 | 69 | end; 70 | 71 | TSubscribersFinder = class(TObject) 72 | class function FindSubscriberMethods(ASubscriberClass: TClass; 73 | ARaiseExcIfEmpty: Boolean = false): TArray; 74 | end; 75 | 76 | implementation 77 | 78 | uses 79 | RTTIUtilsU, System.SysUtils, System.TypInfo; 80 | 81 | { TSubscriberMethod } 82 | 83 | constructor TSubscriberMethod.Create(ARttiMethod: TRttiMethod; 84 | AEventType: TClass; AThreadMode: TThreadMode; const AContext: String = ''; 85 | APriority: Integer = 1); 86 | begin 87 | FMethod := ARttiMethod; 88 | FEventType := AEventType; 89 | FThreadMode := AThreadMode; 90 | FContext := AContext; 91 | end; 92 | 93 | destructor TSubscriberMethod.Destroy; 94 | begin 95 | inherited; 96 | end; 97 | 98 | function TSubscriberMethod.Equals(Obj: TObject): Boolean; 99 | var 100 | OtherSubscriberMethod: TSubscriberMethod; 101 | begin 102 | if (inherited Equals(Obj)) then 103 | exit(true) 104 | else if (Obj is TSubscriberMethod) then 105 | begin 106 | OtherSubscriberMethod := TSubscriberMethod(Obj); 107 | exit(OtherSubscriberMethod.Method.ToString = Method.ToString); 108 | end 109 | else 110 | exit(false); 111 | end; 112 | 113 | procedure TSubscriberMethod.SetContext(const Value: String); 114 | begin 115 | FContext := Value; 116 | end; 117 | 118 | procedure TSubscriberMethod.SetEventType(const Value: TClass); 119 | begin 120 | FEventType := Value; 121 | end; 122 | 123 | procedure TSubscriberMethod.SetMethod(const Value: TRttiMethod); 124 | begin 125 | FMethod := Value; 126 | end; 127 | 128 | procedure TSubscriberMethod.SetThreadMode(const Value: TThreadMode); 129 | begin 130 | FThreadMode := Value; 131 | end; 132 | 133 | { TSubscribersFinder } 134 | 135 | class function TSubscribersFinder.FindSubscriberMethods(ASubscriberClass 136 | : TClass; ARaiseExcIfEmpty: Boolean = false): TArray; 137 | var 138 | LRttiType: TRttiType; 139 | LSubscribeAttribute: SubscribeAttribute; 140 | LRttiMethods: TArray; 141 | LMethod: TRttiMethod; 142 | LParamsLength: Integer; 143 | LEventType: TClass; 144 | LSubMethod: TSubscriberMethod; 145 | begin 146 | LRttiType := TRTTIUtils.ctx.GetType(ASubscriberClass); 147 | LRttiMethods := LRttiType.GetMethods; 148 | for LMethod in LRttiMethods do 149 | if TRTTIUtils.HasAttribute(LMethod, LSubscribeAttribute) 150 | then 151 | begin 152 | LParamsLength := Length(LMethod.GetParameters); 153 | if (LParamsLength <> 1) then 154 | raise Exception.CreateFmt 155 | ('Method %s has Subscribe attribute but requires %d arguments. Methods must require a single argument.', 156 | [LMethod.Name, LParamsLength]); 157 | LEventType := LMethod.GetParameters[0].ParamType.Handle.TypeData. 158 | ClassType; 159 | LSubMethod := TSubscriberMethod.Create(LMethod, LEventType, 160 | LSubscribeAttribute.ThreadMode, LSubscribeAttribute.Context); 161 | {$IF CompilerVersion >= 28.0} 162 | Result := Result + [LSubMethod]; 163 | {$ELSE} 164 | SetLength(Result, Length(Result) + 1); 165 | Result[High(Result)] := LSubMethod; 166 | {$ENDIF} 167 | end; 168 | //if (Length(Result) < 1) and ARaiseExcIfEmpty then 169 | // raise Exception.CreateFmt 170 | // ('The class %s and its super classes have no public methods with the Subscribe attributes', 171 | // [ASubscriberClass.QualifiedClassName]); 172 | end; 173 | 174 | { TSubscription } 175 | 176 | constructor TSubscription.Create(ASubscriber: TObject; 177 | ASubscriberMethod: TSubscriberMethod); 178 | begin 179 | inherited Create; 180 | FSubscriber := ASubscriber; 181 | FSubscriberMethod := ASubscriberMethod; 182 | FActive := true; 183 | end; 184 | 185 | destructor TSubscription.Destroy; 186 | begin 187 | if Assigned(FSubscriberMethod) then 188 | FreeAndNil(FSubscriberMethod); 189 | inherited; 190 | end; 191 | 192 | function TSubscription.Equals(Obj: TObject): Boolean; 193 | var 194 | LOtherSubscription: TSubscription; 195 | begin 196 | if (Obj is TSubscription) then 197 | begin 198 | LOtherSubscription := TSubscription(Obj); 199 | exit((Subscriber = LOtherSubscription.Subscriber) and 200 | (SubscriberMethod.Equals(LOtherSubscription.SubscriberMethod))); 201 | end 202 | else 203 | exit(false); 204 | end; 205 | 206 | function TSubscription.GetActive: Boolean; 207 | begin 208 | TMonitor.Enter(self); 209 | try 210 | Result := FActive; 211 | finally 212 | TMonitor.exit(self); 213 | end; 214 | end; 215 | 216 | function TSubscription.GetContext: String; 217 | begin 218 | Result := SubscriberMethod.Context; 219 | end; 220 | 221 | procedure TSubscription.SetActive(const Value: Boolean); 222 | begin 223 | TMonitor.Enter(self); 224 | try 225 | FActive := Value; 226 | finally 227 | TMonitor.exit(self); 228 | end; 229 | end; 230 | 231 | procedure TSubscription.SetSubscriberMethod(const Value: TSubscriberMethod); 232 | begin 233 | FSubscriberMethod := Value; 234 | end; 235 | 236 | procedure TSubscription.SetSubscriber(const Value: TObject); 237 | begin 238 | FSubscriber := Value; 239 | end; 240 | 241 | end. 242 | -------------------------------------------------------------------------------- /src/RTTIUtilsU.pas: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // 3 | // Delphi MVC Framework 4 | // 5 | // Copyright (c) 2010-2016 Daniele Teti and the DMVCFramework Team 6 | // 7 | // https://github.com/danieleteti/delphimvcframework 8 | // 9 | // *************************************************************************** 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | // 23 | // *************************************************************************** 24 | 25 | unit RTTIUtilsU; 26 | 27 | interface 28 | 29 | uses 30 | RTTI, 31 | DB, 32 | Generics.Collections, 33 | System.SysUtils; 34 | 35 | type 36 | TRTTIUtils = class sealed 37 | public 38 | class var ctx: TRttiContext; 39 | class var TValueToStringFormatSettings: TFormatSettings; 40 | 41 | public 42 | class function MethodCall(AObject: TObject; AMethodName: string; AParameters: array of TValue; 43 | RaiseExceptionIfNotFound: boolean = true): TValue; 44 | class function GetMethod(AObject: TObject; AMethodName: string): TRttiMethod; 45 | class procedure SetProperty(Obj: TObject; const PropertyName: string; const Value: TValue); overload; static; 46 | class function GetFieldType(AProp: TRttiProperty): string; 47 | class function GetPropertyType(AObject: TObject; APropertyName: string): string; 48 | class procedure ObjectToDataSet(Obj: TObject; Field: TField; var Value: Variant); 49 | class function ExistsProperty(AObject: TObject; const APropertyName: string; out AProperty: TRttiProperty): boolean; 50 | class procedure DatasetToObject(Dataset: TDataset; Obj: TObject); 51 | class function GetProperty(Obj: TObject; const PropertyName: string): TValue; 52 | class function GetPropertyAsString(Obj: TObject; const PropertyName: string): string; overload; 53 | 54 | class function GetPropertyAsString(Obj: TObject; AProperty: TRttiProperty): string; overload; 55 | class function GetField(Obj: TObject; const PropertyName: string): TValue; overload; 56 | class procedure SetField(Obj: TObject; const PropertyName: string; const Value: TValue); overload; 57 | class function Clone(Obj: TObject): TObject; static; 58 | class procedure CopyObject(SourceObj, TargetObj: TObject); static; 59 | {$IF CompilerVersion >= 24.0} // not supported in xe3 60 | class procedure CopyObjectAS(SourceObj, TargetObj: TObject); static; 61 | {$IFEND} 62 | class function CreateObject(ARttiType: TRttiType): TObject; overload; static; 63 | class function CreateObject(AQualifiedClassName: string): TObject; overload; static; 64 | class function GetAttribute(const Obj: TRttiObject): T; overload; 65 | class function GetAttribute(const Obj: TRttiType): T; overload; 66 | 67 | class function HasAttribute(const Obj: TRttiObject): boolean; overload; 68 | class function HasAttribute(const Obj: TRttiObject; out AAttribute: T): boolean; overload; 69 | class function HasAttribute(aObj: TObject; out AAttribute: T): boolean; overload; 70 | class function HasAttribute(ARTTIMember: TRttiMember; out AAttribute: T): boolean; overload; 71 | class function HasAttribute(ARTTIMember: TRttiType; out AAttribute: T): boolean; overload; 72 | 73 | class function TValueAsString(const Value: TValue; const PropertyType, CustomFormat: string): string; 74 | class function EqualValues(source, destination: TValue): boolean; 75 | class function FindByProperty(List: TObjectList; PropertyName: string; PropertyValue: TValue): T; 76 | class procedure ForEachProperty(Clazz: TClass; Proc: TProc); 77 | class function HasStringValueAttribute(ARTTIMember: TRttiMember; out Value: string): boolean; 78 | class function BuildClass(AQualifiedName: string; Params: array of TValue): TObject; 79 | class function FindType(QualifiedName: string): TRttiType; 80 | class function GetGUID: TGUID; 81 | 82 | end; 83 | 84 | function FieldFor(const PropertyName: string): string; inline; 85 | 86 | implementation 87 | 88 | uses 89 | Classes, 90 | TypInfo, 91 | ObjectsMappers, 92 | DuckListU; 93 | 94 | class function TRTTIUtils.MethodCall(AObject: TObject; AMethodName: string; AParameters: array of TValue; 95 | RaiseExceptionIfNotFound: boolean): TValue; 96 | var 97 | m: TRttiMethod; 98 | T: TRttiType; 99 | Found: boolean; 100 | ParLen: Integer; 101 | MethodParamsLen: Integer; 102 | begin 103 | Found := False; 104 | T := ctx.GetType(AObject.ClassInfo); 105 | ParLen := Length(AParameters); 106 | m := nil; 107 | for m in T.GetMethods do 108 | begin 109 | MethodParamsLen := Length(m.GetParameters); 110 | if m.Name.Equals(AMethodName) and (MethodParamsLen = ParLen) then 111 | begin 112 | Found := true; 113 | Break; 114 | end; 115 | end; 116 | 117 | if Found then 118 | Result := m.Invoke(AObject, AParameters) 119 | else if RaiseExceptionIfNotFound then 120 | raise Exception.CreateFmt('Cannot find compatible method "%s" in the object', [AMethodName]); 121 | end; 122 | 123 | function FieldFor(const PropertyName: string): string; inline; 124 | begin 125 | Result := 'F' + PropertyName; 126 | end; 127 | 128 | class function TRTTIUtils.GetAttribute(const Obj: TRttiObject): T; 129 | var 130 | Attr: TCustomAttribute; 131 | begin 132 | Result := nil; 133 | for Attr in Obj.GetAttributes do 134 | begin 135 | if Attr.ClassType.InheritsFrom(T) then 136 | Exit(T(Attr)); 137 | end; 138 | end; 139 | 140 | class function TRTTIUtils.GetAttribute(const Obj: TRttiType): T; 141 | var 142 | Attr: TCustomAttribute; 143 | begin 144 | Result := nil; 145 | for Attr in Obj.GetAttributes do 146 | begin 147 | if Attr.ClassType.InheritsFrom(T) then 148 | Exit(T(Attr)); 149 | end; 150 | end; 151 | 152 | class function TRTTIUtils.GetField(Obj: TObject; const PropertyName: string): TValue; 153 | var 154 | Field: TRttiField; 155 | Prop: TRttiProperty; 156 | ARttiType: TRttiType; 157 | begin 158 | ARttiType := ctx.GetType(Obj.ClassType); 159 | if not Assigned(ARttiType) then 160 | raise Exception.CreateFmt('Cannot get RTTI for type [%s]', [ARttiType.ToString]); 161 | Field := ARttiType.GetField(FieldFor(PropertyName)); 162 | if Assigned(Field) then 163 | Result := Field.GetValue(Obj) 164 | else 165 | begin 166 | Prop := ARttiType.GetProperty(PropertyName); 167 | if not Assigned(Prop) then 168 | raise Exception.CreateFmt('Cannot get RTTI for property [%s.%s]', [ARttiType.ToString, PropertyName]); 169 | Result := Prop.GetValue(Obj); 170 | end; 171 | end; 172 | 173 | class function TRTTIUtils.GetProperty(Obj: TObject; const PropertyName: string): TValue; 174 | var 175 | Prop: TRttiProperty; 176 | ARttiType: TRttiType; 177 | begin 178 | ARttiType := ctx.GetType(Obj.ClassType); 179 | if not Assigned(ARttiType) then 180 | raise Exception.CreateFmt('Cannot get RTTI for type [%s]', [ARttiType.ToString]); 181 | Prop := ARttiType.GetProperty(PropertyName); 182 | if not Assigned(Prop) then 183 | raise Exception.CreateFmt('Cannot get RTTI for property [%s.%s]', [ARttiType.ToString, PropertyName]); 184 | if Prop.IsReadable then 185 | Result := Prop.GetValue(Obj) 186 | else 187 | raise Exception.CreateFmt('Property is not readable [%s.%s]', [ARttiType.ToString, PropertyName]); 188 | end; 189 | 190 | class function TRTTIUtils.GetPropertyAsString(Obj: TObject; AProperty: TRttiProperty): string; 191 | var 192 | P: TValue; 193 | FT: string; 194 | CustomFormat: string; 195 | begin 196 | if AProperty.IsReadable then 197 | begin 198 | P := AProperty.GetValue(Obj); 199 | FT := GetFieldType(AProperty); 200 | HasStringValueAttribute(AProperty, CustomFormat); 201 | Result := TValueAsString(P, FT, CustomFormat); 202 | end 203 | else 204 | Result := ''; 205 | end; 206 | 207 | class function TRTTIUtils.GetPropertyAsString(Obj: TObject; const PropertyName: string): string; 208 | var 209 | Prop: TRttiProperty; 210 | begin 211 | Prop := ctx.GetType(Obj.ClassType).GetProperty(PropertyName); 212 | if Assigned(Prop) then 213 | Result := GetPropertyAsString(Obj, Prop) 214 | else 215 | Result := ''; 216 | end; 217 | 218 | class function TRTTIUtils.GetPropertyType(AObject: TObject; APropertyName: string): string; 219 | begin 220 | Result := GetFieldType(ctx.GetType(AObject.ClassInfo).GetProperty(APropertyName)); 221 | end; 222 | 223 | class function TRTTIUtils.HasAttribute(const Obj: TRttiObject): boolean; 224 | begin 225 | Result := Assigned(GetAttribute(Obj)); 226 | end; 227 | 228 | class function TRTTIUtils.HasAttribute(ARTTIMember: TRttiMember; out AAttribute: T): boolean; 229 | var 230 | attrs: TArray; 231 | Attr: TCustomAttribute; 232 | begin 233 | AAttribute := nil; 234 | Result := False; 235 | attrs := ARTTIMember.GetAttributes; 236 | for Attr in attrs do 237 | if Attr is T then 238 | begin 239 | AAttribute := T(Attr); 240 | Exit(true); 241 | end; 242 | end; 243 | 244 | class function TRTTIUtils.HasAttribute(ARTTIMember: TRttiType; out AAttribute: T): boolean; 245 | var 246 | attrs: TArray; 247 | Attr: TCustomAttribute; 248 | begin 249 | AAttribute := nil; 250 | Result := False; 251 | attrs := ARTTIMember.GetAttributes; 252 | for Attr in attrs do 253 | if Attr is T then 254 | begin 255 | AAttribute := T(Attr); 256 | Exit(true); 257 | end; 258 | 259 | end; 260 | 261 | class function TRTTIUtils.HasAttribute(const Obj: TRttiObject; out AAttribute: T): boolean; 262 | begin 263 | AAttribute := GetAttribute(Obj); 264 | Result := Assigned(AAttribute); 265 | end; 266 | 267 | class function TRTTIUtils.HasStringValueAttribute(ARTTIMember: TRttiMember; out Value: string): boolean; 268 | var 269 | Attr: T; // StringValueAttribute; 270 | begin 271 | Result := HasAttribute(ARTTIMember, Attr); 272 | if Result then 273 | Value := StringValueAttribute(Attr).Value 274 | else 275 | Value := ''; 276 | end; 277 | 278 | class procedure TRTTIUtils.SetField(Obj: TObject; const PropertyName: string; const Value: TValue); 279 | var 280 | Field: TRttiField; 281 | Prop: TRttiProperty; 282 | ARttiType: TRttiType; 283 | begin 284 | ARttiType := ctx.GetType(Obj.ClassType); 285 | if not Assigned(ARttiType) then 286 | raise Exception.CreateFmt('Cannot get RTTI for type [%s]', [ARttiType.ToString]); 287 | Field := ARttiType.GetField(FieldFor(PropertyName)); 288 | if Assigned(Field) then 289 | Field.SetValue(Obj, Value) 290 | else 291 | begin 292 | Prop := ARttiType.GetProperty(PropertyName); 293 | if Assigned(Prop) then 294 | begin 295 | if Prop.IsWritable then 296 | Prop.SetValue(Obj, Value) 297 | end 298 | else 299 | raise Exception.CreateFmt('Cannot get RTTI for field or property [%s.%s]', [ARttiType.ToString, PropertyName]); 300 | end; 301 | end; 302 | 303 | class procedure TRTTIUtils.SetProperty(Obj: TObject; const PropertyName: string; const Value: TValue); 304 | var 305 | Prop: TRttiProperty; 306 | ARttiType: TRttiType; 307 | begin 308 | ARttiType := ctx.GetType(Obj.ClassType); 309 | if not Assigned(ARttiType) then 310 | raise Exception.CreateFmt('Cannot get RTTI for type [%s]', [ARttiType.ToString]); 311 | Prop := ARttiType.GetProperty(PropertyName); 312 | if not Assigned(Prop) then 313 | raise Exception.CreateFmt('Cannot get RTTI for property [%s.%s]', [ARttiType.ToString, PropertyName]); 314 | if Prop.IsWritable then 315 | Prop.SetValue(Obj, Value) 316 | else 317 | raise Exception.CreateFmt('Property is not writeable [%s.%s]', [ARttiType.ToString, PropertyName]); 318 | end; 319 | 320 | class function TRTTIUtils.TValueAsString(const Value: TValue; const PropertyType, CustomFormat: string): string; 321 | begin 322 | case Value.Kind of 323 | tkUnknown: 324 | Result := ''; 325 | tkInteger: 326 | Result := IntToStr(Value.AsInteger); 327 | tkChar: 328 | Result := Value.AsString; 329 | tkEnumeration: 330 | if PropertyType = 'boolean' then 331 | Result := BoolToStr(Value.AsBoolean, true) 332 | else 333 | Result := '(enumeration)'; 334 | tkFloat: 335 | begin 336 | if PropertyType = 'datetime' then 337 | begin 338 | if CustomFormat = '' then 339 | Exit(DateTimeToStr(Value.AsExtended)) 340 | else 341 | Exit(FormatDateTime(CustomFormat, Value.AsExtended)) 342 | end 343 | else if PropertyType = 'date' then 344 | begin 345 | if CustomFormat = '' then 346 | Exit(DateToStr(Value.AsExtended)) 347 | else 348 | Exit(FormatDateTime(CustomFormat, Trunc(Value.AsExtended))) 349 | end 350 | else if PropertyType = 'time' then 351 | begin 352 | if CustomFormat = '' then 353 | Exit(TimeToStr(Value.AsExtended)) 354 | else 355 | Exit(FormatDateTime(CustomFormat, Frac(Value.AsExtended))) 356 | end; 357 | if CustomFormat.IsEmpty then 358 | Result := FloatToStr(Value.AsExtended) 359 | else 360 | Result := FormatFloat(CustomFormat, Value.AsExtended); 361 | end; 362 | tkString: 363 | Result := Value.AsString; 364 | tkSet: 365 | ; 366 | tkClass: 367 | Result := Value.AsObject.QualifiedClassName; 368 | tkMethod: 369 | ; 370 | tkWChar: 371 | Result := Value.AsString; 372 | 373 | tkLString: 374 | Result := Value.AsString; 375 | 376 | tkWString: 377 | Result := Value.AsString; 378 | 379 | tkVariant: 380 | Result := string(Value.AsVariant); 381 | 382 | tkArray: 383 | Result := '(array)'; 384 | tkRecord: 385 | Result := '(record)'; 386 | tkInterface: 387 | Result := '(interface)'; 388 | 389 | tkInt64: 390 | Result := IntToStr(Value.AsInt64); 391 | 392 | tkDynArray: 393 | Result := '(array)'; 394 | 395 | tkUString: 396 | Result := Value.AsString; 397 | tkClassRef: 398 | Result := '(classref)'; 399 | 400 | tkPointer: 401 | Result := '(pointer)'; 402 | 403 | tkProcedure: 404 | Result := '(procedure)'; 405 | end; 406 | end; 407 | 408 | class function TRTTIUtils.GetFieldType(AProp: TRttiProperty): string; 409 | var 410 | _PropInfo: PTypeInfo; 411 | begin 412 | _PropInfo := AProp.PropertyType.Handle; 413 | if _PropInfo.Kind in [tkString, tkWString, tkChar, tkWChar, tkLString, tkUString] then 414 | Result := 'string' 415 | else if _PropInfo.Kind in [tkInteger, tkInt64] then 416 | Result := 'integer' 417 | else if _PropInfo = TypeInfo(TDate) then 418 | Result := 'date' 419 | else if _PropInfo = TypeInfo(TDateTime) then 420 | Result := 'datetime' 421 | else if _PropInfo = TypeInfo(Currency) then 422 | Result := 'decimal' 423 | else if _PropInfo = TypeInfo(TTime) then 424 | begin 425 | Result := 'time' 426 | end 427 | else if _PropInfo.Kind = tkFloat then 428 | begin 429 | Result := 'float' 430 | end 431 | else if (_PropInfo.Kind = tkEnumeration) { and (_PropInfo.Name = 'Boolean') } then 432 | Result := 'boolean' 433 | else if AProp.PropertyType.IsInstance and AProp.PropertyType.AsInstance.MetaclassType.InheritsFrom(TStream) then 434 | Result := 'blob' 435 | else 436 | Result := EmptyStr; 437 | end; 438 | 439 | class function TRTTIUtils.GetGUID: TGUID; 440 | var 441 | Tp: TRttiType; 442 | begin 443 | Tp := ctx.GetType(TypeInfo(T)); 444 | if not (Tp.TypeKind = tkInterface) then 445 | raise Exception.Create('Type is no interface'); 446 | Result := TRttiInterfaceType(Tp).GUID; 447 | end; 448 | 449 | class function TRTTIUtils.GetMethod(AObject: TObject; AMethodName: string): TRttiMethod; 450 | var 451 | T: TRttiType; 452 | begin 453 | T := ctx.GetType(AObject.ClassInfo); 454 | Result := T.GetMethod(AMethodName); 455 | end; 456 | 457 | class procedure TRTTIUtils.ObjectToDataSet(Obj: TObject; Field: TField; var Value: Variant); 458 | begin 459 | Value := GetProperty(Obj, Field.FieldName).AsVariant; 460 | end; 461 | 462 | class procedure TRTTIUtils.DatasetToObject(Dataset: TDataset; Obj: TObject); 463 | var 464 | ARttiType: TRttiType; 465 | props: TArray; 466 | Prop: TRttiProperty; 467 | f: TField; 468 | begin 469 | ARttiType := ctx.GetType(Obj.ClassType); 470 | props := ARttiType.GetProperties; 471 | for Prop in props do 472 | if not SameText(Prop.Name, 'ID') then 473 | begin 474 | f := Dataset.FindField(Prop.Name); 475 | if Assigned(f) and not f.ReadOnly then 476 | begin 477 | if f is TIntegerField then 478 | SetProperty(Obj, Prop.Name, TIntegerField(f).Value) 479 | else 480 | SetProperty(Obj, Prop.Name, TValue.From(f.Value)) 481 | end; 482 | end; 483 | end; 484 | 485 | class function TRTTIUtils.EqualValues(source, destination: TValue): boolean; 486 | begin 487 | // Really UniCodeCompareStr (Annoying VCL Name for backwards compatablity) 488 | Result := AnsiCompareStr(source.ToString, destination.ToString) = 0; 489 | end; 490 | 491 | class function TRTTIUtils.ExistsProperty(AObject: TObject; const APropertyName: string; out AProperty: TRttiProperty): boolean; 492 | begin 493 | AProperty := ctx.GetType(AObject.ClassInfo).GetProperty(APropertyName); 494 | Result := Assigned(AProperty); 495 | end; 496 | 497 | class function TRTTIUtils.FindByProperty(List: TObjectList; PropertyName: string; PropertyValue: TValue): T; 498 | var 499 | elem: T; 500 | V: TValue; 501 | Found: boolean; 502 | begin 503 | Found := False; 504 | for elem in List do 505 | begin 506 | V := GetProperty(elem, PropertyName); 507 | case V.Kind of 508 | tkInteger: 509 | Found := V.AsInteger = PropertyValue.AsInteger; 510 | tkFloat: 511 | Found := abs(V.AsExtended - PropertyValue.AsExtended) < 0.001; 512 | tkString, tkLString, tkWString, tkUString: 513 | Found := V.AsString = PropertyValue.AsString; 514 | tkInt64: 515 | Found := V.AsInt64 = PropertyValue.AsInt64; 516 | else 517 | raise Exception.Create('Property type not supported'); 518 | end; 519 | if Found then 520 | Exit(elem); 521 | end; 522 | Result := nil; 523 | end; 524 | 525 | class function TRTTIUtils.FindType(QualifiedName: string): TRttiType; 526 | begin 527 | Result := ctx.FindType(QualifiedName); 528 | end; 529 | 530 | class procedure TRTTIUtils.ForEachProperty(Clazz: TClass; Proc: TProc); 531 | var 532 | _rtti: TRttiType; 533 | P: TRttiProperty; 534 | begin 535 | _rtti := ctx.GetType(Clazz); 536 | if Assigned(_rtti) then 537 | begin 538 | for P in _rtti.GetProperties do 539 | Proc(P); 540 | end; 541 | end; 542 | 543 | class procedure TRTTIUtils.CopyObject(SourceObj, TargetObj: TObject); 544 | var 545 | _ARttiType: TRttiType; 546 | Field: TRttiField; 547 | master, cloned: TObject; 548 | Src: TObject; 549 | sourceStream: TStream; 550 | SavedPosition: Int64; 551 | targetStream: TStream; 552 | targetCollection: IWrappedList; 553 | sourceCollection: IWrappedList; 554 | I: Integer; 555 | sourceObject: TObject; 556 | targetObject: TObject; 557 | Tar: TObject; 558 | begin 559 | if not Assigned(TargetObj) then 560 | Exit; 561 | 562 | _ARttiType := ctx.GetType(SourceObj.ClassType); 563 | cloned := TargetObj; 564 | master := SourceObj; 565 | for Field in _ARttiType.GetFields do 566 | begin 567 | if not Field.FieldType.IsInstance then 568 | Field.SetValue(cloned, Field.GetValue(master)) 569 | else 570 | begin 571 | Src := Field.GetValue(SourceObj).AsObject; 572 | if Src is TStream then 573 | begin 574 | sourceStream := TStream(Src); 575 | SavedPosition := sourceStream.Position; 576 | sourceStream.Position := 0; 577 | if Field.GetValue(cloned).IsEmpty then 578 | begin 579 | targetStream := TMemoryStream.Create; 580 | Field.SetValue(cloned, targetStream); 581 | end 582 | else 583 | targetStream := Field.GetValue(cloned).AsObject as TStream; 584 | targetStream.Position := 0; 585 | targetStream.CopyFrom(sourceStream, sourceStream.Size); 586 | targetStream.Position := SavedPosition; 587 | sourceStream.Position := SavedPosition; 588 | end 589 | else if TDuckTypedList.CanBeWrappedAsList(Src) then 590 | begin 591 | sourceCollection := WrapAsList(Src); 592 | Tar := Field.GetValue(cloned).AsObject; 593 | if Assigned(Tar) then 594 | begin 595 | targetCollection := WrapAsList(Tar); 596 | targetCollection.Clear; 597 | for I := 0 to sourceCollection.Count - 1 do 598 | targetCollection.Add(TRTTIUtils.Clone(sourceCollection.GetItem(I))); 599 | end; 600 | end 601 | else 602 | begin 603 | sourceObject := Src; 604 | 605 | if Field.GetValue(cloned).IsEmpty then 606 | begin 607 | targetObject := TRTTIUtils.Clone(sourceObject); 608 | Field.SetValue(cloned, targetObject); 609 | end 610 | else 611 | begin 612 | targetObject := Field.GetValue(cloned).AsObject; 613 | TRTTIUtils.CopyObject(sourceObject, targetObject); 614 | end; 615 | end; 616 | end; 617 | end; 618 | end; 619 | 620 | {$IF CompilerVersion >= 24.0} 621 | 622 | class procedure TRTTIUtils.CopyObjectAS(SourceObj, TargetObj: TObject); 623 | var 624 | _ARttiType: TRttiType; 625 | _ARttiTypeTarget: TRttiType; 626 | Field, FieldDest: TRttiField; 627 | master, cloned: TObject; 628 | Src: TObject; 629 | sourceStream: TStream; 630 | SavedPosition: Int64; 631 | targetStream: TStream; 632 | targetCollection: IWrappedList; 633 | sourceCollection: IWrappedList; 634 | I: Integer; 635 | sourceObject: TObject; 636 | targetObject: TObject; 637 | Tar: TObject; 638 | begin 639 | if not Assigned(TargetObj) then 640 | Exit; 641 | 642 | _ARttiType := ctx.GetType(SourceObj.ClassType); 643 | _ARttiTypeTarget := ctx.GetType(TargetObj.ClassType); 644 | 645 | cloned := TargetObj; 646 | master := SourceObj; 647 | for Field in _ARttiType.GetFields do 648 | begin 649 | FieldDest := _ARttiTypeTarget.GetField(Field.Name); 650 | if not Assigned(FieldDest) then 651 | continue; 652 | if not Field.FieldType.IsInstance then 653 | begin 654 | FieldDest.SetValue(cloned, Field.GetValue(master)); 655 | end 656 | else 657 | begin 658 | Src := Field.GetValue(SourceObj).AsObject; 659 | if not Assigned(Src) then 660 | begin 661 | FieldDest.SetValue(cloned, Src); 662 | 663 | end 664 | else if Src is TStream then 665 | begin 666 | sourceStream := TStream(Src); 667 | SavedPosition := sourceStream.Position; 668 | sourceStream.Position := 0; 669 | if FieldDest.GetValue(cloned).IsEmpty then 670 | begin 671 | targetStream := TMemoryStream.Create; 672 | FieldDest.SetValue(cloned, targetStream); 673 | end 674 | else 675 | targetStream := FieldDest.GetValue(cloned).AsObject as TStream; 676 | targetStream.Position := 0; 677 | targetStream.CopyFrom(sourceStream, sourceStream.Size); 678 | targetStream.Position := SavedPosition; 679 | sourceStream.Position := SavedPosition; 680 | end 681 | else if TDuckTypedList.CanBeWrappedAsList(Src) then 682 | begin 683 | sourceCollection := WrapAsList(Src); 684 | Tar := FieldDest.GetValue(cloned).AsObject; 685 | if Assigned(Tar) then 686 | begin 687 | targetCollection := WrapAsList(Tar); 688 | targetCollection.Clear; 689 | for I := 0 to sourceCollection.Count - 1 do 690 | targetCollection.Add(TRTTIUtils.Clone(sourceCollection.GetItem(I))); 691 | end; 692 | end 693 | else 694 | begin 695 | sourceObject := Src; 696 | 697 | if FieldDest.GetValue(cloned).IsEmpty then 698 | begin 699 | targetObject := TRTTIUtils.Clone(sourceObject); 700 | FieldDest.SetValue(cloned, targetObject); 701 | end 702 | else 703 | begin 704 | targetObject := FieldDest.GetValue(cloned).AsObject; 705 | TRTTIUtils.CopyObject(sourceObject, targetObject); 706 | end; 707 | end; 708 | end; 709 | end; 710 | end; 711 | {$IFEND} 712 | 713 | class function TRTTIUtils.CreateObject(AQualifiedClassName: string): TObject; 714 | var 715 | rttitype: TRttiType; 716 | begin 717 | rttitype := ctx.FindType(AQualifiedClassName); 718 | if Assigned(rttitype) then 719 | Result := CreateObject(rttitype) 720 | else 721 | raise Exception.Create('Cannot find RTTI for ' + AQualifiedClassName + '. Hint: Is the specified classtype linked in the module?'); 722 | end; 723 | 724 | class function TRTTIUtils.CreateObject(ARttiType: TRttiType): TObject; 725 | var 726 | Method: TRttiMethod; 727 | metaClass: TClass; 728 | begin 729 | { First solution, clear and slow } 730 | metaClass := nil; 731 | Method := nil; 732 | for Method in ARttiType.GetMethods do 733 | if Method.HasExtendedInfo and Method.IsConstructor then 734 | if Length(Method.GetParameters) = 0 then 735 | begin 736 | metaClass := ARttiType.AsInstance.MetaclassType; 737 | Break; 738 | end; 739 | if Assigned(metaClass) then 740 | Result := Method.Invoke(metaClass, []).AsObject 741 | else 742 | raise Exception.Create('Cannot find a propert constructor for ' + ARttiType.ToString); 743 | 744 | { Second solution, dirty and fast } 745 | // Result := TObject(ARttiType.GetMethod('Create') 746 | // .Invoke(ARttiType.AsInstance.MetaclassType, []).AsObject); 747 | end; 748 | 749 | class function TRTTIUtils.BuildClass(AQualifiedName: string; Params: array of TValue): TObject; 750 | var 751 | T: TRttiType; 752 | V: TValue; 753 | begin 754 | 755 | T := FindType(AQualifiedName); 756 | V := T.GetMethod('Create').Invoke(T.AsInstance.MetaclassType, Params); 757 | Result := V.AsObject; 758 | end; 759 | 760 | class function TRTTIUtils.Clone(Obj: TObject): TObject; 761 | var 762 | _ARttiType: TRttiType; 763 | Field: TRttiField; 764 | master, cloned: TObject; 765 | Src: TObject; 766 | sourceStream: TStream; 767 | SavedPosition: Int64; 768 | targetStream: TStream; 769 | targetCollection: TObjectList; 770 | sourceCollection: TObjectList; 771 | I: Integer; 772 | sourceObject: TObject; 773 | targetObject: TObject; 774 | begin 775 | Result := nil; 776 | if not Assigned(Obj) then 777 | Exit; 778 | 779 | _ARttiType := ctx.GetType(Obj.ClassType); 780 | cloned := CreateObject(_ARttiType); 781 | master := Obj; 782 | for Field in _ARttiType.GetFields do 783 | begin 784 | if not Field.FieldType.IsInstance then 785 | Field.SetValue(cloned, Field.GetValue(master)) 786 | else 787 | begin 788 | Src := Field.GetValue(Obj).AsObject; 789 | if Src is TStream then 790 | begin 791 | sourceStream := TStream(Src); 792 | SavedPosition := sourceStream.Position; 793 | sourceStream.Position := 0; 794 | if Field.GetValue(cloned).IsEmpty then 795 | begin 796 | targetStream := TMemoryStream.Create; 797 | Field.SetValue(cloned, targetStream); 798 | end 799 | else 800 | targetStream := Field.GetValue(cloned).AsObject as TStream; 801 | targetStream.Position := 0; 802 | targetStream.CopyFrom(sourceStream, sourceStream.Size); 803 | targetStream.Position := SavedPosition; 804 | sourceStream.Position := SavedPosition; 805 | end 806 | else if Src is TObjectList then 807 | begin 808 | sourceCollection := TObjectList(Src); 809 | if Field.GetValue(cloned).IsEmpty then 810 | begin 811 | targetCollection := TObjectList.Create; 812 | Field.SetValue(cloned, targetCollection); 813 | end 814 | else 815 | targetCollection := Field.GetValue(cloned).AsObject as TObjectList; 816 | for I := 0 to sourceCollection.Count - 1 do 817 | begin 818 | targetCollection.Add(TRTTIUtils.Clone(sourceCollection[I])); 819 | end; 820 | end 821 | else 822 | begin 823 | sourceObject := Src; 824 | 825 | if Field.GetValue(cloned).IsEmpty then 826 | begin 827 | targetObject := TRTTIUtils.Clone(sourceObject); 828 | Field.SetValue(cloned, targetObject); 829 | end 830 | else 831 | begin 832 | targetObject := Field.GetValue(cloned).AsObject; 833 | TRTTIUtils.CopyObject(sourceObject, targetObject); 834 | end; 835 | Field.SetValue(cloned, targetObject); 836 | end; 837 | end; 838 | 839 | end; 840 | Result := cloned; 841 | end; 842 | 843 | { TListDuckTyping } 844 | 845 | class function TRTTIUtils.HasAttribute(aObj: TObject; out AAttribute: T): boolean; 846 | begin 847 | Result := HasAttribute(ctx.GetType(aObj.ClassType), AAttribute) 848 | end; 849 | 850 | end. 851 | -------------------------------------------------------------------------------- /src/Router4D.Helper.pas: -------------------------------------------------------------------------------- 1 | unit Router4D.Helper; 2 | 3 | interface 4 | 5 | uses 6 | System.Classes, 7 | Vcl.ExtCtrls, 8 | Vcl.Forms, 9 | Vcl.Controls; 10 | 11 | type 12 | TRouter4DHelper = class helper for TPanel 13 | public 14 | procedure RemoveObject; overload; 15 | procedure AddObject(AValue: TForm); 16 | end; 17 | 18 | implementation 19 | 20 | procedure TRouter4DHelper.AddObject(AValue: TForm); 21 | begin 22 | AValue.Parent := Self; 23 | AValue.Show; 24 | end; 25 | 26 | procedure TRouter4DHelper.RemoveObject; 27 | var 28 | lIndex: Integer; 29 | begin 30 | for lIndex := Self.ControlCount - 1 downto 0 do 31 | begin 32 | if (Self.Controls[lIndex] is TForm) then 33 | begin 34 | (Self.Controls[lIndex] as TForm).Close; 35 | (Self.Controls[lIndex] as TForm).parent := nil; 36 | end; 37 | end; 38 | end; 39 | 40 | end. 41 | -------------------------------------------------------------------------------- /src/Router4D.History.pas: -------------------------------------------------------------------------------- 1 | unit Router4D.History; 2 | 3 | {$I Router4D.inc} 4 | 5 | interface 6 | 7 | uses 8 | Classes, 9 | SysUtils, 10 | {$IFDEF HAS_FMX} 11 | FMX.Forms, 12 | FMX.Types, 13 | {$ELSE} 14 | Vcl.Forms, 15 | Vcl.ExtCtrls, 16 | {$ENDIF} 17 | System.Generics.Collections, 18 | Router4D.Interfaces, 19 | Router4D.Props; 20 | 21 | type 22 | TCachePersistent = record 23 | FPatch : String; 24 | FisVisible : Boolean; 25 | FSBKey : String; 26 | FPersistentClass : TPersistentClass; 27 | end; 28 | 29 | TRouter4DHistory = class 30 | private 31 | FListCache : TObjectDictionary; 32 | {$IFDEF HAS_FMX} 33 | FListCacheContainer : TObjectDictionary; 34 | FMainRouter : TFMXObject; 35 | FIndexRouter : TFMXObject; 36 | {$ELSE} 37 | FListCacheContainer : TObjectDictionary; 38 | FMainRouter : TPanel; 39 | FIndexRouter : TPanel; 40 | {$ENDIF} 41 | FListCache2 : TDictionary; 42 | FInstanteObject : iRouter4DComponent; 43 | FListCacheOrder : TList; 44 | FIndexCache : Integer; 45 | FMaxCacheHistory : Integer; 46 | const MAX_FRAME_COUNT = 25; 47 | procedure CreateInstancePersistent( aPath : String); 48 | //procedure CacheKeyNotify(Sender: TObject; const Key: string; Action: TCollectionNotification); 49 | public 50 | constructor Create; 51 | destructor Destroy; override; 52 | {$IFDEF HAS_FMX} 53 | function MainRouter ( aValue : TFMXObject ) : TRouter4DHistory; overload; 54 | function MainRouter : TFMXObject; overload; 55 | function IndexRouter ( aValue : TFMXObject ) : TRouter4DHistory; overload; 56 | function IndexRouter : TFMXObject; overload; 57 | function AddHistoryConteiner ( aKey : String; aObject : TFMXObject) : TRouter4DHistory; overload; 58 | function GetHistoryContainer ( aKey : String ) : TFMXObject; 59 | function GetRouter : String; 60 | function PreviousRouter : String; 61 | {$ELSE} 62 | function MainRouter ( aValue : TPanel ) : TRouter4DHistory; overload; 63 | function MainRouter : TPanel; overload; 64 | function IndexRouter ( aValue : TPanel ) : TRouter4DHistory; overload; 65 | function IndexRouter : TPanel; overload; 66 | function AddHistoryConteiner ( aKey : String; aObject : TPanel) : TRouter4DHistory; overload; 67 | function GetHistoryContainer ( aKey : String ) : TPanel; 68 | {$ENDIF} 69 | function AddHistory ( aKey : String; aObject : TObject ) : iRouter4DComponent; overload; 70 | function AddHistory ( aKey : String; aObject : TPersistentClass ) : iRouter4DComponent; overload; 71 | function AddHistory ( aKey : String; aObject : TPersistentClass; aSBKey : String; isVisible : Boolean ) : iRouter4DComponent; overload; 72 | function RemoveHistory ( aKey : String ) : TRouter4DHistory; 73 | function GetHistory ( aKey : String ) : iRouter4DComponent; 74 | function RoutersList : TDictionary; 75 | function RoutersListPersistent : TDictionary; 76 | function InstanteObject : iRouter4DComponent; 77 | function GoBack : String; 78 | function BreadCrumb(aDelimiter: char = '/') : String; 79 | function addCacheHistory(aKey : String) : TRouter4DHistory; 80 | function IndexCache : Integer; 81 | end; 82 | 83 | var 84 | Router4DHistory : TRouter4DHistory; 85 | 86 | implementation 87 | 88 | { TRouter4DHistory } 89 | 90 | {$IFDEF HAS_FMX} 91 | function TRouter4DHistory.MainRouter(aValue: TFMXObject): TRouter4DHistory; 92 | begin 93 | Result := Self; 94 | FMainRouter := aValue; 95 | end; 96 | 97 | function TRouter4DHistory.MainRouter: TFMXObject; 98 | begin 99 | Result := FMainRouter; 100 | end; 101 | 102 | function TRouter4DHistory.IndexRouter(aValue: TFMXObject): TRouter4DHistory; 103 | begin 104 | Result := Self; 105 | FIndexRouter := aValue; 106 | end; 107 | 108 | function TRouter4DHistory.IndexRouter: TFMXObject; 109 | begin 110 | Result := FIndexRouter; 111 | end; 112 | 113 | function TRouter4DHistory.AddHistoryConteiner( aKey : String; aObject : TFMXObject) : TRouter4DHistory; 114 | var 115 | auxObject : TFMXObject; 116 | begin 117 | Result := Self; 118 | if not FListCacheContainer.TryGetValue(aKey, auxObject) then 119 | FListCacheContainer.Add(aKey, aObject); 120 | end; 121 | 122 | function TRouter4DHistory.GetHistoryContainer(aKey: String): TFMXObject; 123 | begin 124 | FListCacheContainer.TryGetValue(aKey, Result); 125 | end; 126 | 127 | function TRouter4DHistory.PreviousRouter: String; 128 | begin 129 | Result := Self.FListCacheOrder[Self.FIndexCache - 1]; 130 | end; 131 | 132 | function TRouter4DHistory.GetRouter: String; 133 | begin 134 | Result := Self.FListCacheOrder[Self.FIndexCache]; 135 | end; 136 | {$ELSE} 137 | function TRouter4DHistory.MainRouter(aValue: TPanel): TRouter4DHistory; 138 | begin 139 | Result := Self; 140 | FMainRouter := aValue; 141 | end; 142 | 143 | function TRouter4DHistory.MainRouter: TPanel; 144 | begin 145 | Result := FMainRouter; 146 | end; 147 | 148 | function TRouter4DHistory.IndexRouter(aValue: TPanel): TRouter4DHistory; 149 | begin 150 | Result := Self; 151 | FIndexRouter := aValue; 152 | end; 153 | 154 | function TRouter4DHistory.IndexRouter: TPanel; 155 | begin 156 | Result := FIndexRouter; 157 | end; 158 | 159 | function TRouter4DHistory.AddHistoryConteiner( aKey : String; aObject : TPanel) : TRouter4DHistory; 160 | var 161 | auxObject : TPanel; 162 | begin 163 | Result := Self; 164 | if not FListCacheContainer.TryGetValue(aKey, auxObject) then 165 | FListCacheContainer.Add(aKey, aObject); 166 | end; 167 | 168 | function TRouter4DHistory.GetHistoryContainer(aKey: String): TPanel; 169 | begin 170 | FListCacheContainer.TryGetValue(aKey, Result); 171 | end; 172 | 173 | {$ENDIF} 174 | 175 | function TRouter4DHistory.IndexCache: Integer; 176 | begin 177 | Result := Self.FIndexCache; 178 | end; 179 | 180 | function TRouter4DHistory.BreadCrumb(aDelimiter: char): String; 181 | var 182 | i : integer; 183 | begin 184 | Result := ''; 185 | 186 | if Self.FIndexCache = -1 then 187 | Exit; 188 | 189 | Result := Self.FListCacheOrder[Self.FIndexCache]; 190 | 191 | for i := Self.FIndexCache-1 downto 0 do 192 | begin 193 | Result := Self.FListCacheOrder[i] + ADelimiter + Result; 194 | end; 195 | end; 196 | 197 | function TRouter4DHistory.GoBack: String; 198 | begin 199 | if Self.FIndexCache > 0 then 200 | Dec(Self.FIndexCache); 201 | 202 | Result := Self.FListCacheOrder[Self.FIndexCache]; 203 | end; 204 | 205 | function TRouter4DHistory.AddHistory( aKey : String; aObject : TObject ) : iRouter4DComponent; 206 | var 207 | mKey : String; 208 | vObject : TObject; 209 | begin 210 | if not Supports(aObject, iRouter4DComponent, Result) then 211 | raise Exception.Create('Form not Implement iRouter4DelphiComponent Interface'); 212 | 213 | try GlobalEventBus.RegisterSubscriber(aObject); except end; 214 | 215 | if FListCache.Count > MAX_FRAME_COUNT then 216 | for mKey in FListCache.Keys do 217 | begin 218 | FListCache.Remove(mKey); 219 | Break; 220 | end; 221 | 222 | 223 | if not FListCache.TryGetValue(aKey, vObject) then 224 | FListCache.Add(aKey, aObject); 225 | 226 | end; 227 | 228 | function TRouter4DHistory.AddHistory(aKey: String; 229 | aObject: TPersistentClass): iRouter4DComponent; 230 | var 231 | CachePersistent : TCachePersistent; 232 | vPesersistentClass : TCachePersistent; 233 | begin 234 | CachePersistent.FPatch := aKey; 235 | CachePersistent.FisVisible := True; 236 | CachePersistent.FPersistentClass := aObject; 237 | CachePersistent.FSBKey := 'SBIndex'; 238 | 239 | if not FListCache2.TryGetValue(aKey, vPesersistentClass) then 240 | FListCache2.Add(aKey, CachePersistent); 241 | end; 242 | 243 | function TRouter4DHistory.addCacheHistory(aKey: String): TRouter4DHistory; 244 | var 245 | I: Integer; 246 | begin 247 | Result := Self; 248 | for I := Pred(FListCacheOrder.Count) downto Succ(FIndexCache) do 249 | FListCacheOrder.Delete(I); 250 | 251 | if FListCacheOrder.Count > FMaxCacheHistory then 252 | FListCacheOrder.Delete(0); 253 | 254 | FListCacheOrder.Add(aKey); 255 | FIndexCache := Pred(FListCacheOrder.Count); 256 | end; 257 | 258 | function TRouter4DHistory.AddHistory(aKey: String; aObject: TPersistentClass; 259 | aSBKey : String; isVisible: Boolean): iRouter4DComponent; 260 | var 261 | CachePersistent : TCachePersistent; 262 | vPesersistentClass : TCachePersistent; 263 | begin 264 | CachePersistent.FPatch := aKey; 265 | CachePersistent.FisVisible := isVisible; 266 | CachePersistent.FPersistentClass := aObject; 267 | CachePersistent.FSBKey := aSBKey; 268 | 269 | if not FListCache2.TryGetValue(aKey, vPesersistentClass) then 270 | FListCache2.Add(aKey, CachePersistent); 271 | end; 272 | 273 | constructor TRouter4DHistory.Create; 274 | begin 275 | FListCache := TObjectDictionary.Create; 276 | FListCache2 := TDictionary.Create; 277 | FListCacheOrder := TList.Create; 278 | FMaxCacheHistory := 10; 279 | {$IFDEF HAS_FMX} 280 | FListCacheContainer := TObjectDictionary.Create; 281 | {$ELSE} 282 | FListCacheContainer := TObjectDictionary.Create; 283 | {$ENDIF} 284 | end; 285 | 286 | procedure TRouter4DHistory.CreateInstancePersistent( aPath : String); 287 | var 288 | aPersistentClass : TCachePersistent; 289 | begin 290 | if not FListCache2.TryGetValue(aPath, aPersistentClass) then 291 | raise Exception.Create('Not Register Router ' + aPath); 292 | 293 | Self.AddHistory( 294 | aPath, 295 | TComponentClass( 296 | FindClass( 297 | aPersistentClass 298 | .FPersistentClass 299 | .ClassName 300 | ) 301 | ).Create(Application) 302 | ); 303 | end; 304 | 305 | destructor TRouter4DHistory.Destroy; 306 | begin 307 | FListCache.Free; 308 | FListCache2.Free; 309 | FListCacheContainer.Free; 310 | FListCacheOrder.Free; 311 | inherited; 312 | end; 313 | 314 | function TRouter4DHistory.GetHistory(aKey: String): iRouter4DComponent; 315 | var 316 | aObject : TObject; 317 | begin 318 | 319 | if not FListCache.TryGetValue(aKey, aObject) then 320 | Self.CreateInstancePersistent(aKey); 321 | 322 | if not Supports(FListCache.Items[aKey], iRouter4DComponent, Result) then 323 | raise Exception.Create('Object not Implements Interface Component'); 324 | 325 | FInstanteObject := Result; 326 | end; 327 | 328 | function TRouter4DHistory.InstanteObject: iRouter4DComponent; 329 | begin 330 | Result := FInstanteObject; 331 | end; 332 | 333 | function TRouter4DHistory.RemoveHistory(aKey: String): TRouter4DHistory; 334 | begin 335 | Result := Self; 336 | FListCache.Remove(aKey); 337 | end; 338 | 339 | function TRouter4DHistory.RoutersList: TDictionary; 340 | begin 341 | Result := FListCache; 342 | end; 343 | 344 | function TRouter4DHistory.RoutersListPersistent: TDictionary; 345 | begin 346 | Result := FListCache2; 347 | end; 348 | 349 | initialization 350 | Router4DHistory := TRouter4DHistory.Create; 351 | 352 | finalization 353 | Router4DHistory.Free; 354 | end. 355 | -------------------------------------------------------------------------------- /src/Router4D.Interfaces.pas: -------------------------------------------------------------------------------- 1 | unit Router4D.Interfaces; 2 | 3 | {$I Router4D.inc} 4 | 5 | interface 6 | 7 | uses 8 | System.Classes, 9 | System.Generics.Collections, 10 | System.UITypes, 11 | SysUtils, 12 | {$IFDEF HAS_FMX} 13 | FMX.Types, 14 | {$ELSE} 15 | Vcl.ExtCtrls, 16 | Vcl.Forms, 17 | {$ENDIF} 18 | Router4D.Props; 19 | 20 | type 21 | 22 | iRouter4D = interface 23 | ['{56BF88E9-25AB-49C7-8CB2-F89C95F34816}'] 24 | end; 25 | 26 | iRouter4DComponent = interface 27 | ['{C605AEFB-36DC-4952-A3D9-BA372B998BC3}'] 28 | {$IFDEF HAS_FMX} 29 | function Render : TFMXObject; 30 | {$ElSE} 31 | function Render : TForm; 32 | {$ENDIF} 33 | procedure UnRender; 34 | end; 35 | 36 | iRouter4DComponentProps = interface 37 | ['{FAF5DD55-924F-4A8B-A436-208891FFE30A}'] 38 | procedure Props ( aProps : TProps ); 39 | end; 40 | 41 | iRouter4DLink = interface 42 | ['{3C80F86A-D6B8-470C-A30E-A82E620F6F1D}'] 43 | {$IFDEF HAS_FMX} 44 | function &To ( aPatch : String; aComponent : TFMXObject ) : iRouter4DLink; overload; 45 | function &To ( aPatch : String; aProps : TProps; aComponent : TFMXObject ) : iRouter4DLink; overload; 46 | function Animation ( aAnimation : TProc ) : iRouter4DLink; 47 | {$ELSE} 48 | function &To ( aPatch : String; aComponent : TPanel ) : iRouter4DLink; overload; 49 | function &To ( aPatch : String; aProps : TProps; aComponent : TPanel ) : iRouter4DLink; overload; 50 | function Animation ( aAnimation : TProc ) : iRouter4DLink; 51 | {$ENDIF} 52 | function &To ( aPatch : String) : iRouter4DLink; overload; 53 | function &To ( aPatch : String; aProps : TProps; aKey : String = '') : iRouter4DLink; overload; 54 | function &To ( aPatch : String; aNameContainer : String) : iRouter4DLink; overload; 55 | function IndexLink ( aPatch : String ) : iRouter4DLink; 56 | function GoBack : iRouter4DLink; 57 | end; 58 | 59 | iRouter4DRender = interface 60 | ['{2BD026ED-3A92-44E9-8CD4-38E80CB2F000}'] 61 | {$IFDEF HAS_FMX} 62 | function SetElement ( aComponent : TFMXObject; aIndexComponent : TFMXObject = nil ) : iRouter4DRender; 63 | {$ELSE} 64 | function SetElement ( aComponent : TPanel; aIndexComponent : TPanel = nil ) : iRouter4DRender; 65 | {$ENDIF} 66 | end; 67 | 68 | iRouter4DSwitch = interface 69 | ['{0E49AFE7-9329-4F0C-B289-A713FA3DFE45}'] 70 | function Router(aPath : String; aRouter : TPersistentClass; aSidebarKey : String = 'SBIndex'; isVisible : Boolean = True) : iRouter4DSwitch; 71 | function UnRouter(aPath : String) : iRouter4DSwitch; 72 | end; 73 | 74 | iRouter4DSidebar = interface 75 | ['{B4E8C229-A801-4FCA-AF7B-DEF8D0EE5DFE}'] 76 | function Name ( aValue : String ) : iRouter4DSidebar; overload; 77 | {$IFDEF HAS_FMX} 78 | function MainContainer ( aValue : TFMXObject ) : iRouter4DSidebar; overload; 79 | function MainContainer : TFMXObject; overload; 80 | function LinkContainer ( aValue : TFMXObject ) : iRouter4DSidebar; 81 | function Animation ( aAnimation : TProc ) : iRouter4DSidebar; 82 | function RenderToListBox : iRouter4DSidebar; 83 | {$ELSE} 84 | function MainContainer ( aValue : TPanel ) : iRouter4DSidebar; overload; 85 | function MainContainer : TPanel; overload; 86 | function LinkContainer ( aValue : TPanel ) : iRouter4DSidebar; 87 | function Animation ( aAnimation : TProc ) : iRouter4DSidebar; 88 | {$ENDIF} 89 | function Name : String; overload; 90 | function FontSize ( aValue : Integer ) : iRouter4DSidebar; 91 | function FontColor ( aValue : TAlphaColor ) : iRouter4DSidebar; 92 | function ItemHeigth ( aValue : Integer ) : iRouter4DSidebar; 93 | 94 | end; 95 | 96 | implementation 97 | 98 | end. 99 | -------------------------------------------------------------------------------- /src/Router4D.Link.pas: -------------------------------------------------------------------------------- 1 | unit Router4D.Link; 2 | 3 | {$I Router4D.inc} 4 | 5 | interface 6 | 7 | uses 8 | {$IFDEF HAS_FMX} 9 | FMX.Types, 10 | FMX.Layouts, 11 | {$ELSE} 12 | Vcl.ExtCtrls, 13 | Router4D.Helper, 14 | {$ENDIF} 15 | SysUtils, 16 | Router4D.Interfaces, 17 | Router4D.Props; 18 | 19 | type 20 | TRouter4DLink = class(TInterfacedObject, iRouter4DLink) 21 | private 22 | {$IFDEF HAS_FMX} 23 | FAnimation : TProc; 24 | {$ELSE} 25 | FAnimation : TProc; 26 | {$ENDIF} 27 | public 28 | constructor Create; 29 | destructor Destroy; override; 30 | class function New : iRouter4DLink; 31 | {$IFDEF HAS_FMX} 32 | function Animation ( aAnimation : TProc ) : iRouter4DLink; 33 | function &To ( aPatch : String; aComponent : TFMXObject ) : iRouter4DLink; overload; 34 | function &To ( aPatch : String; aProps: TProps; aComponent : TFMXObject ) : iRouter4DLink; overload; 35 | {$ELSE} 36 | function Animation ( aAnimation : TProc ) : iRouter4DLink; 37 | function &To ( aPatch : String; aComponent : TPanel ) : iRouter4DLink; overload; 38 | function &To ( aPatch : String; aProps : TProps; aComponent : TPanel ) : iRouter4DLink; overload; 39 | {$ENDIF} 40 | function &To ( aPatch : String) : iRouter4DLink; overload; 41 | function &To ( aPatch : String; aProps : TProps; aKey : String = '') : iRouter4DLink; overload; 42 | function &To ( aPatch : String; aNameContainer : String) : iRouter4DLink; overload; 43 | function GoBack : iRouter4DLink; 44 | function IndexLink ( aPatch : String ) : iRouter4DLink; 45 | end; 46 | 47 | var 48 | Router4DLink : iRouter4DLink; 49 | 50 | implementation 51 | 52 | { TRouter4DLink } 53 | 54 | 55 | uses Router4D.History; 56 | 57 | {$IFDEF HAS_FMX} 58 | function TRouter4DLink.Animation(aAnimation: TProc): iRouter4DLink; 59 | begin 60 | Result := Self; 61 | FAnimation := aAnimation; 62 | end; 63 | 64 | function TRouter4DLink.&To( aPatch : String; aComponent : TFMXObject ) : iRouter4DLink; 65 | begin 66 | Result := Self; 67 | aComponent.RemoveObject(0); 68 | Router4DHistory.InstanteObject.UnRender; 69 | aComponent 70 | .AddObject( 71 | Router4DHistory 72 | .addCacheHistory(aPatch) 73 | .GetHistory(aPatch) 74 | .Render 75 | ); 76 | end; 77 | 78 | function TRouter4DLink.&To( aPatch : String; aProps: TProps; aComponent : TFMXObject ) : iRouter4DLink; 79 | begin 80 | Result := Self; 81 | aComponent.RemoveObject(0); 82 | Router4DHistory.InstanteObject.UnRender; 83 | aComponent 84 | .AddObject( 85 | Router4DHistory 86 | .addCacheHistory(aPatch) 87 | .GetHistory(aPatch) 88 | .Render 89 | ); 90 | 91 | GlobalEventBus.Post(aProps); 92 | end; 93 | {$ELSE} 94 | function TRouter4DLink.Animation(aAnimation: TProc): iRouter4DLink; 95 | begin 96 | Result := Self; 97 | FAnimation := aAnimation; 98 | end; 99 | 100 | function TRouter4DLink.&To( aPatch : String; aComponent : TPanel ) : iRouter4DLink; 101 | begin 102 | Result := Self; 103 | aComponent.RemoveObject; 104 | Router4DHistory.InstanteObject.UnRender; 105 | aComponent 106 | .AddObject( 107 | Router4DHistory 108 | .addCacheHistory(aPatch) 109 | .GetHistory(aPatch) 110 | .Render 111 | ); 112 | end; 113 | 114 | function TRouter4DLink.&To( aPatch : String; aProps: TProps; aComponent : TPanel ) : iRouter4DLink; 115 | begin 116 | Result := Self; 117 | aComponent.RemoveObject; 118 | Router4DHistory.InstanteObject.UnRender; 119 | aComponent 120 | .AddObject( 121 | Router4DHistory 122 | .addCacheHistory(aPatch) 123 | .GetHistory(aPatch) 124 | .Render 125 | ); 126 | 127 | GlobalEventBus.Post(aProps); 128 | end; 129 | {$ENDIF} 130 | 131 | function TRouter4DLink.&To(aPatch, aNameContainer: String): iRouter4DLink; 132 | var 133 | {$IFDEF HAS_FMX} 134 | aContainer : TFMXObject; 135 | {$ELSE} 136 | aContainer : TPanel; 137 | {$ENDIF} 138 | begin 139 | Result := Self; 140 | Router4DHistory.InstanteObject.UnRender; 141 | aContainer := Router4DHistory.GetHistoryContainer(aNameContainer); 142 | {$IFDEF HAS_FMX} 143 | aContainer.RemoveObject(0); 144 | {$ELSE} 145 | aContainer.RemoveObject; 146 | {$ENDIF} 147 | 148 | aContainer 149 | .AddObject( 150 | Router4DHistory 151 | .addCacheHistory(aPatch) 152 | .GetHistory(aPatch) 153 | .Render 154 | ); 155 | 156 | if Assigned(FAnimation) then 157 | FAnimation(aContainer); 158 | 159 | end; 160 | 161 | constructor TRouter4DLink.Create; 162 | begin 163 | 164 | end; 165 | 166 | destructor TRouter4DLink.Destroy; 167 | begin 168 | 169 | inherited; 170 | end; 171 | 172 | function TRouter4DLink.GoBack : iRouter4DLink; 173 | begin 174 | Result := Self; 175 | {$IFDEF HAS_FMX} 176 | Router4DHistory.MainRouter.RemoveObject(0); 177 | {$ELSE} 178 | Router4DHistory.MainRouter.RemoveObject; 179 | {$ENDIF} 180 | Router4DHistory.InstanteObject.UnRender; 181 | Router4DHistory 182 | .MainRouter 183 | .AddObject( 184 | Router4DHistory 185 | .GetHistory(Router4DHistory.GoBack) 186 | .Render 187 | ); 188 | 189 | if Assigned(FAnimation) then 190 | FAnimation(Router4DHistory.MainRouter); 191 | end; 192 | function TRouter4DLink.IndexLink(aPatch: String): iRouter4DLink; 193 | begin 194 | Result := Self; 195 | {$IFDEF HAS_FMX} 196 | Router4DHistory.IndexRouter.RemoveObject(0); 197 | {$ELSE} 198 | Router4DHistory.IndexRouter.RemoveObject; 199 | {$ENDIF} 200 | Router4DHistory.InstanteObject.UnRender; 201 | Router4DHistory 202 | .IndexRouter 203 | .AddObject( 204 | Router4DHistory 205 | .GetHistory(aPatch) 206 | .Render 207 | ); 208 | 209 | if Assigned(FAnimation) then 210 | FAnimation(Router4DHistory.IndexRouter); 211 | 212 | end; 213 | 214 | function TRouter4DLink.&To(aPatch: String) : iRouter4DLink; 215 | begin 216 | Result := Self; 217 | {$IFDEF HAS_FMX} 218 | Router4DHistory.MainRouter.RemoveObject(0); 219 | {$ELSE} 220 | Router4DHistory.MainRouter.RemoveObject; 221 | {$ENDIF} 222 | Router4DHistory.InstanteObject.UnRender; 223 | Router4DHistory 224 | .MainRouter 225 | .AddObject( 226 | Router4DHistory 227 | .addCacheHistory(aPatch) 228 | .GetHistory(aPatch) 229 | .Render 230 | ); 231 | 232 | if Assigned(FAnimation) then 233 | FAnimation(Router4DHistory.MainRouter); 234 | 235 | end; 236 | 237 | function TRouter4DLink.&To(aPatch: String; aProps: TProps; aKey : String = '') : iRouter4DLink; 238 | begin 239 | Result := Self; 240 | {$IFDEF HAS_FMX} 241 | Router4DHistory.MainRouter.RemoveObject(0); 242 | {$ELSE} 243 | Router4DHistory.MainRouter.RemoveObject; 244 | {$ENDIF} 245 | Router4DHistory.InstanteObject.UnRender; 246 | Router4DHistory 247 | .MainRouter 248 | .AddObject( 249 | Router4DHistory 250 | .addCacheHistory(aPatch) 251 | .GetHistory(aPatch) 252 | .Render 253 | ); 254 | 255 | if Assigned(FAnimation) then 256 | FAnimation(Router4DHistory.MainRouter); 257 | 258 | if aKey <> '' then aProps.Key(aKey); 259 | 260 | GlobalEventBus.Post(aProps); 261 | end; 262 | 263 | class function TRouter4DLink.New: iRouter4DLink; 264 | begin 265 | if not Assigned(Router4DLink) then 266 | Router4DLink := Self.Create; 267 | 268 | Result := Router4DLink; 269 | end; 270 | 271 | initialization 272 | Router4DLink := TRouter4DLink.New; 273 | 274 | end. 275 | -------------------------------------------------------------------------------- /src/Router4D.Props.pas: -------------------------------------------------------------------------------- 1 | { ******************************************************************************* 2 | Copyright 2016-2019 Daniele Spinetti 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | ******************************************************************************** } 16 | 17 | unit Router4D.Props; 18 | 19 | {$I Router4D.inc} 20 | 21 | interface 22 | 23 | uses 24 | System.Classes, 25 | System.SysUtils, 26 | System.Rtti; 27 | 28 | type 29 | 30 | TThreadMode = (Posting, Main, Async, Background); 31 | 32 | TCloneEventCallback = function(const AObject: TObject): TObject of object; 33 | TCloneEventMethod = TFunc; 34 | 35 | IEventBus = Interface 36 | ['{7BDF4536-F2BA-4FBA-B186-09E1EE6C7E35}'] 37 | procedure RegisterSubscriber(ASubscriber: TObject); 38 | function IsRegistered(ASubscriber: TObject): Boolean; 39 | procedure Unregister(ASubscriber: TObject); 40 | procedure Post(AEvent: TObject; const AContext: String = ''; 41 | AEventOwner: Boolean = true); 42 | 43 | procedure SetOnCloneEvent(const aCloneEvent: TCloneEventCallback); 44 | procedure AddCustomClassCloning(const AQualifiedClassName: String; 45 | const aCloneEvent: TCloneEventMethod); 46 | procedure RemoveCustomClassCloning(const AQualifiedClassName: String); 47 | 48 | property OnCloneEvent: TCloneEventCallback write SetOnCloneEvent; 49 | end; 50 | 51 | SubscribeAttribute = class(TCustomAttribute) 52 | private 53 | FContext: String; 54 | FThreadMode: TThreadMode; 55 | public 56 | constructor Create(AThreadMode: TThreadMode = TThreadMode.Posting; 57 | const AContext: String = ''); 58 | property ThreadMode: TThreadMode read FThreadMode; 59 | property Context: String read FContext; 60 | end; 61 | 62 | TDEBEvent = class(TObject) 63 | private 64 | FDataOwner: Boolean; 65 | FData: T; 66 | procedure SetData(const Value: T); 67 | procedure SetDataOwner(const Value: Boolean); 68 | public 69 | constructor Create; overload; 70 | constructor Create(AData: T); overload; 71 | destructor Destroy; override; 72 | property DataOwner: Boolean read FDataOwner write SetDataOwner; 73 | property Data: T read FData write SetData; 74 | end; 75 | 76 | 77 | TProps = class 78 | private 79 | FPropString: String; 80 | FPropInteger: Integer; 81 | FPropCurrency : Currency; 82 | FPropDouble : Double; 83 | FPropValue : TValue; 84 | FPropObject : TObject; 85 | FPropDateTime : TDateTime; 86 | FKey : String; 87 | public 88 | constructor Create; 89 | destructor Destroy; override; 90 | function PropString ( aProp : String ) : TProps; overload; 91 | function PropString : String; overload; 92 | function PropInteger ( aProp : Integer ) : TProps; overload; 93 | function PropInteger : Integer; overload; 94 | function PropCurrency ( aProp : Currency ) : TProps; overload; 95 | function PropCurrency : Currency; overload; 96 | function PropDouble ( aProp : Double ) : TProps; overload; 97 | function PropDouble : Double; overload; 98 | function PropValue ( aProp : TValue ) : TProps; overload; 99 | function PropValue : TValue; overload; 100 | function PropObject ( aProp : TObject ) : TProps; overload; 101 | function PropObject : TObject; overload; 102 | function PropDateTime ( aProp : TDateTime ) : TProps; overload; 103 | function PropDateTime : TDateTime; overload; 104 | function Key ( aKey : String ) : TProps; overload; 105 | function Key : String; overload; 106 | end; 107 | 108 | function GlobalEventBus: IEventBus; 109 | 110 | implementation 111 | 112 | uses 113 | EventBus.Core, RTTIUtilsU; 114 | 115 | var 116 | FGlobalEventBus: IEventBus; 117 | 118 | { SubscribeAttribute } 119 | 120 | constructor SubscribeAttribute.Create(AThreadMode 121 | : TThreadMode = TThreadMode.Posting; const AContext: String = ''); 122 | begin 123 | inherited Create; 124 | FContext := AContext; 125 | FThreadMode := AThreadMode; 126 | end; 127 | 128 | { TDEBSimpleEvent } 129 | 130 | constructor TDEBEvent.Create(AData: T); 131 | begin 132 | inherited Create; 133 | DataOwner := true; 134 | Data := AData; 135 | end; 136 | 137 | constructor TDEBEvent.Create; 138 | begin 139 | inherited Create; 140 | end; 141 | 142 | destructor TDEBEvent.Destroy; 143 | var 144 | LValue: TValue; 145 | begin 146 | LValue := TValue.From(Data); 147 | if (LValue.IsObject) and DataOwner then 148 | LValue.AsObject.Free; 149 | inherited; 150 | end; 151 | 152 | procedure TDEBEvent.SetData(const Value: T); 153 | begin 154 | FData := Value; 155 | end; 156 | 157 | procedure TDEBEvent.SetDataOwner(const Value: Boolean); 158 | begin 159 | FDataOwner := Value; 160 | end; 161 | 162 | function GlobalEventBus: IEventBus; 163 | begin 164 | if not Assigned(FGlobalEventBus) then 165 | FGlobalEventBus := TEventBus.Create; 166 | Result := FGlobalEventBus; 167 | end; 168 | 169 | { TProps } 170 | 171 | constructor TProps.Create; 172 | begin 173 | 174 | end; 175 | 176 | destructor TProps.Destroy; 177 | begin 178 | 179 | inherited; 180 | end; 181 | 182 | function TProps.Key(aKey: String): TProps; 183 | begin 184 | Result := Self; 185 | FKey := aKey; 186 | end; 187 | 188 | function TProps.Key: String; 189 | begin 190 | Result := FKey; 191 | end; 192 | 193 | function TProps.PropCurrency: Currency; 194 | begin 195 | Result := FPropCurrency; 196 | end; 197 | 198 | function TProps.PropDateTime: TDateTime; 199 | begin 200 | Result := FPropDateTime; 201 | end; 202 | 203 | function TProps.PropDateTime(aProp: TDateTime): TProps; 204 | begin 205 | Result := Self; 206 | FPropDateTime := aProp; 207 | end; 208 | 209 | function TProps.PropDouble: Double; 210 | begin 211 | Result := FPropDouble; 212 | end; 213 | 214 | function TProps.PropDouble(aProp: Double): TProps; 215 | begin 216 | Result := Self; 217 | FPropDouble := aProp; 218 | end; 219 | 220 | function TProps.PropCurrency(aProp: Currency): TProps; 221 | begin 222 | Result := Self; 223 | FPropCurrency := aProp; 224 | end; 225 | 226 | function TProps.PropInteger: Integer; 227 | begin 228 | Result := FPropInteger; 229 | end; 230 | 231 | function TProps.PropObject: TObject; 232 | begin 233 | Result := FPropObject; 234 | end; 235 | 236 | function TProps.PropObject(aProp: TObject): TProps; 237 | begin 238 | Result := Self; 239 | FPropObject := aProp; 240 | end; 241 | 242 | function TProps.PropInteger(aProp: Integer): TProps; 243 | begin 244 | Result := Self; 245 | FPropInteger := aProp; 246 | end; 247 | 248 | function TProps.PropString(aProp: String): TProps; 249 | begin 250 | Result := Self; 251 | FPropString := aProp; 252 | end; 253 | 254 | function TProps.PropString: String; 255 | begin 256 | Result := FPropString; 257 | end; 258 | 259 | function TProps.PropValue: TValue; 260 | begin 261 | Result := FPropValue; 262 | end; 263 | 264 | function TProps.PropValue(aProp: TValue): TProps; 265 | begin 266 | Result := Self; 267 | FPropValue := aProp; 268 | end; 269 | 270 | initialization 271 | GlobalEventBus; 272 | 273 | finalization 274 | 275 | end. 276 | -------------------------------------------------------------------------------- /src/Router4D.Render.pas: -------------------------------------------------------------------------------- 1 | unit Router4D.Render; 2 | 3 | {$I Router4D.inc} 4 | 5 | interface 6 | 7 | uses 8 | {$IFDEF HAS_FMX} 9 | FMX.Types, 10 | {$ELSE} 11 | Vcl.ExtCtrls, 12 | Router4D.Helper, 13 | {$ENDIF} 14 | Router4D.Interfaces; 15 | 16 | type 17 | TRouter4DRender = class(TInterfacedObject, iRouter4DRender) 18 | private 19 | [weak] 20 | FParent : iRouter4DComponent; 21 | public 22 | constructor Create(Parent : iRouter4DComponent); 23 | destructor Destroy; override; 24 | class function New(Parent : iRouter4DComponent) : iRouter4DRender; 25 | {$IFDEF HAS_FMX} 26 | function SetElement ( aComponent : TFMXObject; aIndexComponent : TFMXObject = nil ) : iRouter4DRender; 27 | {$ELSE} 28 | function SetElement ( aComponent : TPanel; aIndexComponent : TPanel = nil ) : iRouter4DRender; 29 | {$ENDIF} 30 | end; 31 | 32 | implementation 33 | 34 | uses 35 | Router4D.History; 36 | 37 | { TRouter4DelphiRender } 38 | 39 | constructor TRouter4DRender.Create(Parent: iRouter4DComponent); 40 | begin 41 | FParent := Parent; 42 | end; 43 | 44 | destructor TRouter4DRender.Destroy; 45 | begin 46 | 47 | inherited; 48 | end; 49 | 50 | {$IFDEF HAS_FMX} 51 | function TRouter4DRender.SetElement( aComponent : TFMXObject; aIndexComponent : TFMXObject = nil ) : iRouter4DRender; 52 | begin 53 | Result := Self; 54 | Router4DHistory.MainRouter(aComponent); 55 | 56 | if aIndexComponent <> nil then 57 | Router4DHistory.IndexRouter(aIndexComponent); 58 | 59 | if Assigned(FParent) then 60 | begin 61 | aComponent.RemoveObject(0); 62 | aComponent.AddObject(FParent.Render); 63 | end; 64 | end; 65 | {$ELSE} 66 | function TRouter4DRender.SetElement( aComponent : TPanel; aIndexComponent : TPanel = nil ) : iRouter4DRender; 67 | begin 68 | Result := Self; 69 | Router4DHistory.MainRouter(aComponent); 70 | 71 | if aIndexComponent <> nil then 72 | Router4DHistory.IndexRouter(aIndexComponent); 73 | 74 | if Assigned(FParent) then 75 | begin 76 | aComponent.RemoveObject; 77 | aComponent.AddObject(FParent.Render); 78 | end; 79 | end; 80 | {$ENDIF} 81 | 82 | class function TRouter4DRender.New( 83 | Parent: iRouter4DComponent): iRouter4DRender; 84 | begin 85 | Result := Self.Create(Parent); 86 | end; 87 | 88 | end. 89 | -------------------------------------------------------------------------------- /src/Router4D.Sidebar.pas: -------------------------------------------------------------------------------- 1 | unit Router4D.Sidebar; 2 | 3 | {$I Router4D.inc} 4 | 5 | interface 6 | 7 | {$IFDEF HAS_FMX} 8 | uses 9 | Classes, 10 | SysUtils, 11 | FMX.Types, 12 | FMX.ListBox, 13 | FMX.SearchBox, 14 | FMX.Layouts, 15 | Router4D.Interfaces, 16 | System.UITypes; 17 | 18 | type 19 | TRouter4DSidebar = class(TInterfacedObject, iRouter4DSidebar) 20 | private 21 | FName : String; 22 | FMainContainer : TFMXObject; 23 | FLinkContainer : TFMXObject; 24 | FAnimation : TProc; 25 | FFontSize : Integer; 26 | FFontColor : TAlphaColor; 27 | FItemHeigth : Integer; 28 | public 29 | constructor Create; 30 | destructor Destroy; override; 31 | class function New : iRouter4DSidebar; 32 | function Animation ( aAnimation : TProc ) : iRouter4DSidebar; 33 | function MainContainer ( aValue : TFMXObject ) : iRouter4DSidebar; overload; 34 | function MainContainer : TFMXObject; overload; 35 | function LinkContainer ( aValue : TFMXObject ) : iRouter4DSidebar; 36 | function RenderToListBox : iRouter4DSidebar; 37 | function Name ( aValue : String ) : iRouter4DSidebar; overload; 38 | function Name : String; overload; 39 | function FontSize ( aValue : Integer ) : iRouter4DSidebar; 40 | function FontColor ( aValue : TAlphaColor ) : iRouter4DSidebar; 41 | function ItemHeigth ( aValue : Integer ) : iRouter4DSidebar; 42 | end; 43 | 44 | implementation 45 | 46 | uses 47 | Router4D, 48 | Router4D.History, 49 | Router4D.Utils; 50 | 51 | { TRouter4DSidebar } 52 | 53 | function TRouter4DSidebar.Animation( 54 | aAnimation: TProc): iRouter4DSidebar; 55 | begin 56 | Result := Self; 57 | FAnimation := aAnimation; 58 | end; 59 | 60 | function TRouter4DSidebar.LinkContainer(aValue: TFMXObject): iRouter4DSidebar; 61 | begin 62 | Result := Self; 63 | FLinkContainer := aValue; 64 | end; 65 | 66 | function TRouter4DSidebar.MainContainer(aValue: TFMXObject): iRouter4DSidebar; 67 | begin 68 | Result := Self; 69 | FMainContainer := aValue; 70 | end; 71 | 72 | function TRouter4DSidebar.MainContainer: TFMXObject; 73 | begin 74 | Result := FMainContainer; 75 | end; 76 | 77 | function TRouter4DSidebar.RenderToListBox: iRouter4DSidebar; 78 | var 79 | aListBox : TListBox; 80 | aListBoxItem : TListBoxItem; 81 | AListBoxSearch : TSearchBox; 82 | aItem : TCachePersistent; 83 | begin 84 | aListBox := TListBox.Create(FMainContainer); 85 | aListBox.Align := TAlignLayout.Client; 86 | 87 | aListBox.StyleLookup := 'transparentlistboxstyle'; 88 | 89 | aListBox.BeginUpdate; 90 | 91 | AListBoxSearch := TSearchBox.Create(aListBox); 92 | AListBoxSearch.Height := FItemHeigth - 25; 93 | aListBox.ItemHeight := FItemHeigth; 94 | 95 | aListBox.AddObject(AListBoxSearch); 96 | 97 | for aItem in Router4DHistory.RoutersListPersistent.Values do 98 | begin 99 | if AItem.FisVisible and (AItem.FSBKey = FName) then 100 | begin 101 | aListBoxItem := TListBoxItem.Create(aListBox); 102 | aListBoxItem.Parent := aListBox; 103 | aListBoxItem.StyledSettings:=[TStyledSetting.Other]; 104 | aListBoxItem.TextSettings.Font.Size := FFontSize; 105 | aListBoxItem.FontColor := FFontColor; 106 | aListBoxItem.Text := aItem.FPatch; 107 | aListBox.AddObject(aListBoxItem); 108 | end; 109 | end; 110 | aListBox.EndUpdate; 111 | 112 | 113 | Router4DHistory.AddHistoryConteiner(FName, FLinkContainer); 114 | 115 | aListBox.OnClick := 116 | 117 | TNotifyEventWrapper 118 | .AnonProc2NotifyEvent( 119 | aListBox, 120 | procedure(Sender: TObject; Aux : String) 121 | begin 122 | TRouter4D 123 | .Link 124 | .Animation( 125 | procedure ( aObject : TFMXObject ) 126 | begin 127 | TLayout(aObject).Opacity := 0; 128 | TLayout(aObject).AnimateFloat('Opacity', 1, 0.2); 129 | end) 130 | .&To( 131 | (Sender as TListBox).Items[(Sender as TListBox).ItemIndex], 132 | Aux 133 | ) 134 | end, 135 | FName 136 | ); 137 | 138 | FMainContainer.AddObject(aListBox); 139 | end; 140 | 141 | constructor TRouter4DSidebar.Create; 142 | begin 143 | FName := 'SBIndex'; 144 | FLinkContainer := Router4DHistory.MainRouter; 145 | end; 146 | 147 | destructor TRouter4DSidebar.Destroy; 148 | begin 149 | 150 | inherited; 151 | end; 152 | 153 | function TRouter4DSidebar.FontColor(aValue: TAlphaColor): iRouter4DSidebar; 154 | begin 155 | Result := Self; 156 | FFontColor := aValue; 157 | end; 158 | 159 | function TRouter4DSidebar.FontSize(aValue: Integer): iRouter4DSidebar; 160 | begin 161 | Result := Self; 162 | FFontSize := aValue; 163 | end; 164 | 165 | function TRouter4DSidebar.ItemHeigth(aValue: Integer): iRouter4DSidebar; 166 | begin 167 | Result := Self; 168 | FItemHeigth := aValue; 169 | end; 170 | 171 | function TRouter4DSidebar.Name(aValue: String): iRouter4DSidebar; 172 | begin 173 | Result := Self; 174 | FName := aValue; 175 | end; 176 | 177 | function TRouter4DSidebar.Name: String; 178 | begin 179 | Result := FName; 180 | end; 181 | 182 | class function TRouter4DSidebar.New: iRouter4DSidebar; 183 | begin 184 | Result := Self.Create; 185 | end; 186 | {$ELSE} 187 | implementation 188 | {$ENDIF} 189 | 190 | end. 191 | -------------------------------------------------------------------------------- /src/Router4D.Switch.pas: -------------------------------------------------------------------------------- 1 | unit Router4D.Switch; 2 | 3 | {$I Router4D.inc} 4 | 5 | interface 6 | 7 | uses 8 | Classes, 9 | System.Generics.Collections, 10 | Router4D.Interfaces, 11 | Router4D.History; 12 | 13 | type 14 | TRouter4DSwitch = class(TInterfacedObject, iRouter4DSwitch) 15 | private 16 | FSideBarList : TDictionary; 17 | public 18 | constructor Create; 19 | destructor Destroy; override; 20 | class function New : iRouter4DSwitch; 21 | function Router(aPath : String; aRouter : TPersistentClass; aSidebarKey : String = 'SBIndex'; isVisible : Boolean = True) : iRouter4DSwitch; 22 | function UnRouter(aPath : String) : iRouter4DSwitch; 23 | function SidebarAdd ( aPatch : String; aSideBar : iRouter4DSidebar) : iRouter4DSwitch; 24 | function SideBarList : TDictionary; 25 | end; 26 | 27 | implementation 28 | 29 | { TRouter4DSwitch } 30 | 31 | uses 32 | Router4D.Utils; 33 | 34 | constructor TRouter4DSwitch.Create; 35 | begin 36 | FSideBarList := TDictionary.Create; 37 | end; 38 | 39 | destructor TRouter4DSwitch.Destroy; 40 | begin 41 | FSideBarList.Free; 42 | inherited; 43 | end; 44 | 45 | class function TRouter4DSwitch.New: iRouter4DSwitch; 46 | begin 47 | Result := Self.Create; 48 | end; 49 | 50 | function TRouter4DSwitch.Router(aPath : String; aRouter : TPersistentClass; aSidebarKey : String = 'SBIndex'; isVisible : Boolean = True) : iRouter4DSwitch; 51 | begin 52 | Result := Self; 53 | RegisterClass(aRouter); 54 | Router4DHistory.AddHistory(aPath, aRouter, aSidebarKey, isVisible); 55 | end; 56 | 57 | function TRouter4DSwitch.SidebarAdd(aPatch: String; 58 | aSideBar: iRouter4DSidebar): iRouter4DSwitch; 59 | begin 60 | Result := Self; 61 | FSideBarList.Add(aPatch, aSideBar); 62 | end; 63 | 64 | function TRouter4DSwitch.SideBarList: TDictionary; 65 | begin 66 | Result := FSideBarList; 67 | end; 68 | 69 | function TRouter4DSwitch.UnRouter(aPath: String) : iRouter4DSwitch; 70 | begin 71 | Result := Self; 72 | Router4DHistory.RemoveHistory(aPath); 73 | end; 74 | 75 | end. 76 | -------------------------------------------------------------------------------- /src/Router4D.Utils.pas: -------------------------------------------------------------------------------- 1 | unit Router4D.Utils; 2 | 3 | {$I Router4D.inc} 4 | 5 | interface 6 | 7 | uses 8 | System.Rtti, 9 | Router4D.Props, 10 | SysUtils, 11 | Classes; 12 | 13 | type 14 | TRouter4DUtils = class 15 | private 16 | public 17 | class function CreateInstance : T; 18 | end; 19 | 20 | TNotifyEventWrapper = class(TComponent) 21 | private 22 | FProc: TProc; 23 | FAux : String; 24 | public 25 | constructor Create(Owner: TComponent; Proc: TProc; Aux : String = ''); reintroduce; 26 | class function AnonProc2NotifyEvent(Owner: TComponent; Proc: TProc; Aux : String = ''): TNotifyEvent; 27 | published 28 | procedure Event(Sender: TObject); 29 | end; 30 | 31 | implementation 32 | 33 | { TRouter4DUtils } 34 | 35 | class function TRouter4DUtils.CreateInstance: T; 36 | var 37 | AValue: TValue; 38 | ctx: TRttiContext; 39 | rType: TRttiType; 40 | AMethCreate: TRttiMethod; 41 | instanceType: TRttiInstanceType; 42 | begin 43 | ctx := TRttiContext.Create; 44 | rType := ctx.GetType(TypeInfo(T)); 45 | for AMethCreate in rType.GetMethods do 46 | begin 47 | if (AMethCreate.IsConstructor) and (Length(AMethCreate.GetParameters) = 1) then 48 | begin 49 | instanceType := rType.AsInstance; 50 | AValue := AMethCreate.Invoke(instanceType.MetaclassType, [nil]); 51 | Result := AValue.AsType; 52 | 53 | try 54 | GlobalEventBus.RegisterSubscriber(AValue.AsType); 55 | except 56 | 57 | end; 58 | 59 | Exit; 60 | end; 61 | end; 62 | 63 | end; 64 | 65 | { TNotifyEventWrapper } 66 | 67 | class function TNotifyEventWrapper.AnonProc2NotifyEvent(Owner: TComponent; Proc: TProc; Aux : String = ''): TNotifyEvent; 68 | begin 69 | Result := Self.Create(Owner, Proc, Aux).Event; 70 | end; 71 | 72 | constructor TNotifyEventWrapper.Create(Owner: TComponent; Proc: TProc; Aux : String = ''); 73 | begin 74 | inherited Create(Owner); 75 | FProc := Proc; 76 | FAux := Aux; 77 | end; 78 | 79 | procedure TNotifyEventWrapper.Event(Sender: TObject); 80 | begin 81 | FProc(Sender, FAux); 82 | end; 83 | 84 | end. 85 | -------------------------------------------------------------------------------- /src/Router4D.inc: -------------------------------------------------------------------------------- 1 | //{$DEFINE HAS_FMX} -------------------------------------------------------------------------------- /src/Router4D.pas: -------------------------------------------------------------------------------- 1 | unit Router4D; 2 | 3 | {$I Router4D.inc} 4 | 5 | interface 6 | 7 | uses 8 | System.Generics.Collections, 9 | System.Classes, 10 | System.Rtti, 11 | System.TypInfo, 12 | SysUtils, 13 | {$IFDEF HAS_FMX} 14 | FMX.Types, 15 | {$ELSE} 16 | Vcl.ExtCtrls, 17 | {$ENDIF} 18 | Router4D.Interfaces, 19 | Router4D.History, 20 | Router4D.Render, 21 | Router4D.Link; 22 | 23 | type 24 | TRouter4D = class(TInterfacedObject, iRouter4D) 25 | private 26 | public 27 | constructor Create; 28 | destructor Destroy; override; 29 | class function New : iRouter4D; 30 | class function Render : iRouter4DRender; overload; 31 | class function Render(initialRoute:string) : iRouter4DRender; overload; 32 | class function Link : iRouter4DLink; 33 | class function Switch : iRouter4DSwitch; 34 | {$IFDEF HAS_FMX} 35 | class function SideBar : iRouter4DSidebar; 36 | {$ENDIF} 37 | end; 38 | 39 | implementation 40 | 41 | { TRouter4Delphi } 42 | 43 | uses 44 | Router4D.Utils, 45 | Router4D.Switch, 46 | Router4D.Sidebar; 47 | 48 | constructor TRouter4D.Create; 49 | begin 50 | 51 | end; 52 | 53 | destructor TRouter4D.Destroy; 54 | begin 55 | 56 | inherited; 57 | end; 58 | 59 | class function TRouter4D.Link: iRouter4DLink; 60 | begin 61 | Result := TRouter4DLink.New; 62 | end; 63 | 64 | class function TRouter4D.New: iRouter4D; 65 | begin 66 | Result := Self.Create; 67 | end; 68 | 69 | class function TRouter4D.Render(initialRoute:string): iRouter4DRender; 70 | begin 71 | Router4DHistory 72 | .AddHistory( 73 | TPersistentClass(T).ClassName, 74 | TPersistentClass(T) 75 | ); 76 | 77 | 78 | Result := 79 | TRouter4DRender 80 | .New( 81 | Router4DHistory 82 | .addCacheHistory(initialRoute) 83 | .GetHistory( 84 | TPersistentClass(T) 85 | .ClassName 86 | ) 87 | ); 88 | end; 89 | 90 | class function TRouter4D.Render: iRouter4DRender; 91 | begin 92 | Router4DHistory 93 | .AddHistory( 94 | TPersistentClass(T).ClassName, 95 | TPersistentClass(T) 96 | ); 97 | 98 | 99 | Result := 100 | TRouter4DRender 101 | .New( 102 | Router4DHistory 103 | .GetHistory( 104 | TPersistentClass(T) 105 | .ClassName 106 | ) 107 | ); 108 | end; 109 | {$IFDEF HAS_FMX} 110 | class function TRouter4D.SideBar: iRouter4DSidebar; 111 | begin 112 | Result := TRouter4DSidebar.New; 113 | end; 114 | {$ENDIF} 115 | class function TRouter4D.Switch: iRouter4DSwitch; 116 | begin 117 | Result := TRouter4DSwitch.New; 118 | end; 119 | 120 | end. 121 | --------------------------------------------------------------------------------