UltravioletFramework / ultraviolet

The Ultraviolet Framework is a .NET game development framework written in C#.
https://github.com/UltravioletFramework/ultraviolet/wiki
MIT License
542 stars 46 forks source link

Fix NPE for PasswordEditor when ViewModel is reseted #62

Closed Scellow closed 7 years ago

tlgkccampbell commented 7 years ago

I'm having some trouble replicating the exception you found in PasswordEditor. Can you describe a simple scenario which reproduces the problem so that I can test this change locally?

Also, in the future, it would make things easier for me if you could submit changes to separate components as separate pull requests.

Thanks!

tlgkccampbell commented 7 years ago

Can you describe your use case for flattened atlas cell names?

Scellow commented 7 years ago

For the NPE:

Let's stay i have this Screen:

public class LoginScreen : ScreenBase
    {
        public LoginScreen(ContentManager globalContent, UIScreenService uiScreenService) 
            : base("data/ui/screens/login", "LoginScreen", globalContent, uiScreenService)
        {

        }

        protected override Object CreateViewModel(UIView view)
        {
            return new LoginViewModel(this);
        }

        protected override void OnOpening()
        {
            ResetViewModel();
            base.OnOpening();
        }
    }

In the view model if i have something like that:

        public void HandleViewOpened(DependencyObject dobj, RoutedEventData data)
        {
            tbMail?.Focus();
            tbPass?.SetPassword(password);
        }

If i open it, it works, but if i close it and then re-open it later, it'll crash

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at Ultraviolet.Presentation.Controls.Primitives.PasswordEditor.SetPassword(StringSegment value) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Controls\Primitives\PasswordEditor.cs:line 9
8
   at Ultraviolet.Presentation.Controls.Primitives.PasswordEditor.SetPassword(String value) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Controls\Primitives\PasswordEditor.cs:line 72
   at Ultraviolet.Presentation.Controls.PasswordBox.SetPassword(String value) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Controls\PasswordBox.cs:line 69
   at Realm.Client.UI.Screens.LoginViewModel.HandleViewOpened(DependencyObject dobj, RoutedEventData data) in C:\Users\Scellow\Projects\CS\RealmReborn\RealmReborn\src\Realm.Client\UI\Screens\LoginScreen.ViewModel.cs:line 45
   at lambda_method(Closure , DependencyObject , RoutedEventData )
   at lambda_method(Closure , DependencyObject , RoutedEventData )
   at Ultraviolet.Presentation.PresentationFoundationView.RaiseViewLifecycleEvent(DependencyObject dobj, RoutedEvent evt) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\PresentationFoundatio
nView.cs:line 1204
   at Ultraviolet.Presentation.PresentationFoundationView.<>c.<RaiseViewLifecycleEvent>b__82_0(DependencyObject child, Object state) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Presentati
onFoundationView.cs:line 1208
   at Ultraviolet.Presentation.Media.VisualTreeHelper.ForEachChild[TChild](DependencyObject dobj, Object state, Action`2 action) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Media\VisualTr
eeHelper.cs:line 71
   at Ultraviolet.Presentation.Media.VisualTreeHelper.ForEachChild(DependencyObject dobj, Object state, Action`2 action) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Media\VisualTreeHelper
.cs:line 50
   at Ultraviolet.Presentation.PresentationFoundationView.RaiseViewLifecycleEvent(DependencyObject dobj, RoutedEvent evt) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\PresentationFoundatio
nView.cs:line 1206
   at Ultraviolet.Presentation.PresentationFoundationView.<>c.<RaiseViewLifecycleEvent>b__82_0(DependencyObject child, Object state) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Presentati
onFoundationView.cs:line 1208
   at Ultraviolet.Presentation.Media.VisualTreeHelper.ForEachChild[TChild](DependencyObject dobj, Object state, Action`2 action) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Media\VisualTr
eeHelper.cs:line 71
   at Ultraviolet.Presentation.Media.VisualTreeHelper.ForEachChild(DependencyObject dobj, Object state, Action`2 action) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Media\VisualTreeHelper
.cs:line 50
   at Ultraviolet.Presentation.PresentationFoundationView.RaiseViewLifecycleEvent(DependencyObject dobj, RoutedEvent evt) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\PresentationFoundatio
nView.cs:line 1206
   at Ultraviolet.Presentation.PresentationFoundationView.<>c.<RaiseViewLifecycleEvent>b__82_0(DependencyObject child, Object state) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Presentati
onFoundationView.cs:line 1208
   at Ultraviolet.Presentation.Media.VisualTreeHelper.ForEachChild[TChild](DependencyObject dobj, Object state, Action`2 action) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Media\VisualTr
eeHelper.cs:line 71
   at Ultraviolet.Presentation.Media.VisualTreeHelper.ForEachChild(DependencyObject dobj, Object state, Action`2 action) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\Media\VisualTreeHelper
.cs:line 50
   at Ultraviolet.Presentation.PresentationFoundationView.RaiseViewLifecycleEvent(DependencyObject dobj, RoutedEvent evt) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\PresentationFoundatio
nView.cs:line 1206
   at Ultraviolet.Presentation.PresentationFoundationView.OnOpened() in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Presentation\Shared\PresentationFoundationView.cs:line 1064
   at Ultraviolet.UI.UIScreen.HandleOpened() in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet\Shared\UI\UIScreen.cs:line 248
   at Ultraviolet.UI.UIPanel.OpenInternal(TimeSpan duration, Boolean async) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet\Shared\UI\UIPanel.cs:line 834
   at Ultraviolet.UI.UIPanel.Open(Nullable`1 duration) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet\Shared\UI\UIPanel.cs:line 399
   at Ultraviolet.UI.UIScreenStack.Open(UIScreen screen, Nullable`1 duration) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet\Shared\UI\UIScreenStack.cs:line 142
   at Ultraviolet.UI.UIScreenStack.UpdatePendingOpenings() in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet\Shared\UI\UIScreenStack.cs:line 554
   at Ultraviolet.UI.UIScreenStack.Update(UltravioletTime time) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet\Shared\UI\UIScreenStack.cs:line 92
   at Ultraviolet.UI.UltravioletUI.Update(UltravioletTime time) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet\Shared\UI\UltravioletUI.cs:line 37
   at Ultraviolet.SDL2.SDL2UltravioletContext.Update(UltravioletTime time) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.SDL2\Shared\SDL2UltravioletContext.cs:line 64
   at Ultraviolet.UltravioletHostCore.UpdateContext(UltravioletContext uv, UltravioletTime time) in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet\Shared\UltravioletHostCore.cs:line 232
   at Ultraviolet.UltravioletHostCore.RunOneTick() in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet\Shared\UltravioletHostCore.cs:line 144
   at Ultraviolet.UltravioletApplication.Run() in C:\Users\Scellow\Projects\tmp\ultraviolet\Source\Ultraviolet.Shims.Desktop\UltravioletApplication.cs:line 78
   at Realm.Client.Game.Main(String[] args) in C:\Users\Scellow\Projects\CS\RealmReborn\RealmReborn\src\Realm.Client\Game.cs:line 56
Scellow commented 7 years ago

I still can't properly use git sorry about the mixed commits, i'll see how i can separate them

For the TextureAtlass, i have a specific workflow, where i put all my sprites in categories (folders) so it is easier to manage/draw/edit, but i need to be able to retrieve the cell based on the name only, it just make my life easier, other TexturePacker tools i have used in the past had this option i think

tlgkccampbell commented 7 years ago

Looking at it now, I think there are a couple of other controls in UPF which have similar issues. I'm going to take some time tomorrow to see if I can come up with a more comprehensive fix.

The TextureAtlas change should be fine, I'll get it committed tomorrow as well.

To submit your changes as separate pull requests, it's easiest to make those changes in feature branches within your repository. From the command line you can make a new branch with git checkout -b branchname, then publish it to your GitHub fork with git push --set-upstream origin branchname. You can then push to the branch as you would normally. On GitHub each branch can be submitted as a separate PR.

tlgkccampbell commented 7 years ago

I've committed a fix for the NPE to develop and manually merged your TextureAtlas changes.

I did make some further modifications to TextureAtlasProcessor. Strictly speaking, ExportPreprocessed() was the wrong place to implement your new flag, because if you put it there it'll only work if you preprocess the asset. I moved it to LoadImages() so that it will work regardless of how the asset is loaded.

Let me know if you have any problems with it.