Коммит
611dc5b092
18
HotUI.sln
18
HotUI.sln
|
@ -39,14 +39,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{586C3CCB
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotUI.Tests", "tests\HotUI.Tests\HotUI.Tests.csproj", "{EA098E78-E56C-43B2-9D78-D4565EC04D09}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotUI.WPF", "src\HotUI.WPF\HotUI.WPF.csproj", "{F8BA5DE0-AEC0-4423-B036-5F9157E939D0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotUI.UWP.Sample", "sample\HotUI.UWP.Sample\HotUI.UWP.Sample.csproj", "{35664234-EB75-47BA-A082-9F96BDF6FC92}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotUI.WPF.Sample", "sample\HotUI.WPF.Sample\HotUI.WPF.Sample.csproj", "{0FC87DE5-B5E5-4846-894B-824497118143}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotUI.UWP", "src\HotUI.UWP\HotUI.UWP.csproj", "{2D337300-B3E9-4A36-8387-76A9A1100C5B}"
|
||||
EndProject
|
||||
Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "HotUI.WPF", "src\HotUI.WPF\HotUI.WPF.csproj", "{A8D46A59-EFE4-434B-87EE-FC9085CF559A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -271,6 +271,18 @@ Global
|
|||
{2D337300-B3E9-4A36-8387-76A9A1100C5B}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{2D337300-B3E9-4A36-8387-76A9A1100C5B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{2D337300-B3E9-4A36-8387-76A9A1100C5B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Release|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -290,10 +302,10 @@ Global
|
|||
{BCAF5569-30DB-4D44-BF46-DFFE93DDCBD0} = {AB9AD206-4B1E-4B0C-88A2-5C769314E8A4}
|
||||
{1E8FBBC2-9E06-4960-A116-65CDEFEFE11D} = {AB9AD206-4B1E-4B0C-88A2-5C769314E8A4}
|
||||
{EA098E78-E56C-43B2-9D78-D4565EC04D09} = {586C3CCB-82A5-47F0-A099-B9A31BB4EA88}
|
||||
{F8BA5DE0-AEC0-4423-B036-5F9157E939D0} = {AB9AD206-4B1E-4B0C-88A2-5C769314E8A4}
|
||||
{35664234-EB75-47BA-A082-9F96BDF6FC92} = {C3FDCDB7-0C29-472D-A406-E2F21A4B0EAE}
|
||||
{0FC87DE5-B5E5-4846-894B-824497118143} = {C3FDCDB7-0C29-472D-A406-E2F21A4B0EAE}
|
||||
{2D337300-B3E9-4A36-8387-76A9A1100C5B} = {AB9AD206-4B1E-4B0C-88A2-5C769314E8A4}
|
||||
{A8D46A59-EFE4-434B-87EE-FC9085CF559A} = {AB9AD206-4B1E-4B0C-88A2-5C769314E8A4}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {0653DB4A-5BBE-4D78-99B2-DB1C82663246}
|
||||
|
|
|
@ -17,7 +17,10 @@ namespace HotUI.Android.Sample
|
|||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
Page = new MainPage();
|
||||
//#if DEBUG
|
||||
// HotUI.Reload.Init ();
|
||||
//#endif
|
||||
Page = new MainPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,10 @@ namespace HotUI.Mac.Sample
|
|||
_window.ContentViewController = new MainPage().ToViewController();
|
||||
_window.IsVisible = true;
|
||||
_window.MakeKeyAndOrderFront(this);*/
|
||||
}
|
||||
//#if DEBUG
|
||||
// HotUI.Reload.Init ();
|
||||
//#endif
|
||||
}
|
||||
|
||||
public override void WillTerminate(NSNotification notification)
|
||||
{
|
||||
|
|
|
@ -22,7 +22,9 @@ namespace HotUI.iOS.Sample {
|
|||
{
|
||||
// Override point for customization after application launch.
|
||||
// If not required for your application you can safely delete this method
|
||||
|
||||
//#if DEBUG
|
||||
// HotUI.Reload.Init ();
|
||||
//#endif
|
||||
window = new UIWindow {
|
||||
RootViewController = new MainPage ().ToViewController (),
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<MtouchLink>None</MtouchLink>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<MtouchExtraArgs>--enable-repl</MtouchExtraArgs>
|
||||
<MtouchProfiling>true</MtouchProfiling>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
|
||||
|
@ -51,6 +52,7 @@
|
|||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<MtouchExtraArgs>--interpreter</MtouchExtraArgs>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
|
||||
<DebugType>none</DebugType>
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace HotUI.iOS
|
|||
{
|
||||
var cell = DequeueReusableCell(CellType) as ViewCell ?? new ViewCell();
|
||||
var item = _listView?.List[indexPath.Row];
|
||||
var v = _listView?.CellCreator(item);
|
||||
var v = _listView?.CellCreator?.Invoke(item);
|
||||
v.Parent = _listView;
|
||||
cell.SetView(v);
|
||||
return cell;
|
||||
|
@ -38,7 +38,7 @@ namespace HotUI.iOS
|
|||
{
|
||||
_listView = view as ListView;
|
||||
//TODO: Some crude size estimation
|
||||
var v = _listView.CellCreator(_listView.List[0]);
|
||||
var v = _listView?.CellCreator?.Invoke(_listView?.List[0]);
|
||||
EstimatedRowHeight = 200;
|
||||
ReloadData();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace HotUI {
|
|||
public void Navigate (View view)
|
||||
{
|
||||
view.Navigation = this;
|
||||
view.Parent = this;
|
||||
view.UpdateNavigation ();
|
||||
PerformNavigate (view);
|
||||
}
|
||||
public Action<View> PerformNavigate;
|
||||
|
|
|
@ -28,6 +28,10 @@ namespace HotUI {
|
|||
OnParentChange (value);
|
||||
}
|
||||
}
|
||||
internal void UpdateNavigation()
|
||||
{
|
||||
OnParentChange (Navigation);
|
||||
}
|
||||
protected virtual void OnParentChange(View parent)
|
||||
{
|
||||
this.Navigation = parent.Navigation ?? parent as NavigationView;
|
||||
|
@ -37,6 +41,7 @@ namespace HotUI {
|
|||
public View (bool hasConstructors)
|
||||
{
|
||||
ActiveViews.Add (this);
|
||||
HotReloadHelper.Register (this);
|
||||
Context.View = this;
|
||||
State = StateBuilder.CurrentState ?? new State {
|
||||
StateChanged = ResetView
|
||||
|
@ -62,6 +67,8 @@ namespace HotUI {
|
|||
return;
|
||||
viewHandler?.Remove (this);
|
||||
viewHandler = value;
|
||||
if (replacedView != null)
|
||||
replacedView.ViewHandler = value;
|
||||
WillUpdateView ();
|
||||
viewHandler?.SetView (this.GetRenderView());
|
||||
}
|
||||
|
@ -78,17 +85,20 @@ namespace HotUI {
|
|||
}
|
||||
View builtView;
|
||||
public View BuiltView => builtView;
|
||||
internal void Reload () => ResetView ();
|
||||
void ResetView()
|
||||
{
|
||||
if (usedEnvironmentData.Any ())
|
||||
SetEnvironmentFields ();
|
||||
var oldView = builtView;
|
||||
builtView = null;
|
||||
replacedView?.Dispose ();
|
||||
replacedView = null;
|
||||
if (ViewHandler == null)
|
||||
return;
|
||||
ViewHandler.Remove (this);
|
||||
var view = this.GetRenderView ().Diff (oldView);
|
||||
oldView.Dispose ();
|
||||
oldView?.Dispose ();
|
||||
ViewHandler?.SetView (view);
|
||||
}
|
||||
|
||||
|
@ -100,9 +110,19 @@ namespace HotUI {
|
|||
}
|
||||
|
||||
internal View GetView () => GetRenderView ();
|
||||
|
||||
View replacedView;
|
||||
protected virtual View GetRenderView ()
|
||||
{
|
||||
if (replacedView != null)
|
||||
return replacedView.GetRenderView();
|
||||
var replaced = HotReloadHelper.GetReplacedView (this);
|
||||
if(replaced != this) {
|
||||
replaced.Navigation = this.Navigation;
|
||||
replaced.Parent = this.Parent;
|
||||
replacedView = replaced;
|
||||
replacedView.ViewHandler = ViewHandler;
|
||||
return replacedView.GetRenderView();
|
||||
}
|
||||
if (Body == null)
|
||||
return this;
|
||||
if (builtView != null)
|
||||
|
@ -130,6 +150,7 @@ namespace HotUI {
|
|||
{
|
||||
this.SetPropertyValue (property, value);
|
||||
ViewHandler?.UpdateValue (property, value);
|
||||
replacedView?.ViewPropertyChanged (property, value);
|
||||
}
|
||||
|
||||
internal virtual void ContextPropertyChanged(string property, object value)
|
||||
|
@ -181,10 +202,11 @@ namespace HotUI {
|
|||
public void Dispose ()
|
||||
{
|
||||
ActiveViews.Remove (this);
|
||||
HotReloadHelper.UnRegister (this);
|
||||
ViewHandler = null;
|
||||
Body = null;
|
||||
Context.Clear ();
|
||||
State.DisposingObject(this);
|
||||
State?.DisposingObject(this);
|
||||
State = null;
|
||||
OnDisposing ();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||
using HotUI.Services;
|
||||
|
||||
[assembly: InternalsVisibleTo ("HotUI.Tests")]
|
||||
[assembly: InternalsVisibleTo ("HotUI.Reload.Tests")]
|
||||
|
||||
namespace HotUI{
|
||||
public static class Device {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace HotUI {
|
||||
public static class HotReloadHelper {
|
||||
static HotReloadHelper()
|
||||
{
|
||||
IsEnabled = Debugger.IsAttached;
|
||||
}
|
||||
public static bool IsEnabled { get; set; }
|
||||
|
||||
public static void Register(View view, params object[] parameters)
|
||||
{
|
||||
if (!IsEnabled)
|
||||
return;
|
||||
currentViews [view] = parameters;
|
||||
}
|
||||
|
||||
public static void UnRegister(View view)
|
||||
{
|
||||
if (!IsEnabled)
|
||||
return;
|
||||
currentViews.Remove (view);
|
||||
}
|
||||
|
||||
public static View GetReplacedView(View view)
|
||||
{
|
||||
if (!IsEnabled)
|
||||
return view;
|
||||
if (!replacedViews.TryGetValue (view.GetType ().FullName, out var newViewType))
|
||||
return view;
|
||||
|
||||
currentViews.TryGetValue (view, out var parameters);
|
||||
var newView = parameters?.Length > 0 ? Activator.CreateInstance (newViewType, args: parameters) : Activator.CreateInstance (newViewType);
|
||||
//TODO: Apply state!
|
||||
return (View)newView;
|
||||
}
|
||||
|
||||
static Dictionary<string, Type> replacedViews = new Dictionary<string, Type> ();
|
||||
static Dictionary<View, object []> currentViews = new Dictionary<View, object []> ();
|
||||
public static void RegisterReplacedView(string oldViewType, Type newViewType)
|
||||
{
|
||||
if (!IsEnabled)
|
||||
return;
|
||||
replacedViews [oldViewType] = newViewType;
|
||||
}
|
||||
public static void TriggerReload()
|
||||
{
|
||||
var roots = View.ActiveViews.Where (x => x.Parent == null).ToList();
|
||||
foreach(var view in roots) {
|
||||
Device.InvokeOnMainThread (view.Reload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace HotUI.Tests {
|
||||
public class HotReloadTests {
|
||||
|
||||
public class MyOrgView : View {
|
||||
public const string TextValue = "Hello!";
|
||||
public MyOrgView ()
|
||||
{
|
||||
this.Body = () => new Text (TextValue);
|
||||
}
|
||||
}
|
||||
public class MyNewView : View {
|
||||
public const string TextValue = "Goodbye!";
|
||||
public MyNewView ()
|
||||
{
|
||||
this.Body = () => new Text (TextValue);
|
||||
}
|
||||
}
|
||||
|
||||
public HotReloadTests ()
|
||||
{
|
||||
HotReloadHelper.IsEnabled = true;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HotReloadRegisterReplacedViewReplacesView ()
|
||||
{
|
||||
var orgView = new MyOrgView ();
|
||||
var orgText = orgView.GetView () as Text;
|
||||
Assert.Equal (MyOrgView.TextValue, orgText.Value);
|
||||
|
||||
HotReloadHelper.RegisterReplacedView (typeof (MyOrgView).FullName, typeof (MyNewView));
|
||||
var newText = orgView.GetView () as Text;
|
||||
|
||||
Assert.Equal (MyNewView.TextValue, newText.Value);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
using System;
|
||||
using Xunit;
|
||||
|
||||
namespace HotUI.Tests {
|
||||
public class HotReloadWithParameters : TestBase {
|
||||
public class MyOrgView : View {
|
||||
public const string TextValue = "Hello!";
|
||||
public MyOrgView (string text)
|
||||
{
|
||||
HotReloadHelper.Register (this, text);
|
||||
this.Body = () => new Text (text);
|
||||
}
|
||||
}
|
||||
public class MyNewView : View {
|
||||
public MyNewView (string text)
|
||||
{
|
||||
HotReloadHelper.Register (this, text);
|
||||
this.Body = () => new Text (text);
|
||||
}
|
||||
}
|
||||
|
||||
public HotReloadWithParameters ()
|
||||
{
|
||||
HotReloadHelper.IsEnabled = true;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HotReloadRegisterReplacedViewReplacesView ()
|
||||
{
|
||||
var orgView = new MyOrgView (MyOrgView.TextValue);
|
||||
var orgText = orgView.GetView () as Text;
|
||||
Assert.Equal (MyOrgView.TextValue, orgText.Value);
|
||||
|
||||
HotReloadHelper.RegisterReplacedView (typeof (MyOrgView).FullName, typeof (MyNewView));
|
||||
var newText = orgView.GetView () as Text;
|
||||
|
||||
Assert.Equal (MyOrgView.TextValue, newText.Value);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче