From 2d5d9fd1134f716d18326da51803851f86b5d687 Mon Sep 17 00:00:00 2001
From: Kyle Spearrin <kyle.spearrin@gmail.com>
Date: Mon, 4 Jul 2016 22:31:15 -0400
Subject: [PATCH] Added nofooter and noheader options to extendedtable view.

---
 src/App/Controls/ExtendedTableView.cs         |  2 +
 src/App/Pages/HomePage.cs                     |  2 +-
 src/App/Pages/RegisterPage.cs                 | 50 ++++-------
 .../Pages/Settings/SettingsEditFolderPage.cs  | 39 ++++-----
 .../Pages/Tools/ToolsPasswordGeneratorPage.cs | 11 ++-
 src/iOS/Controls/ExtendedTableViewRenderer.cs | 87 +++++++++++++++++++
 6 files changed, 129 insertions(+), 62 deletions(-)

diff --git a/src/App/Controls/ExtendedTableView.cs b/src/App/Controls/ExtendedTableView.cs
index a58255be4..a1a25467c 100644
--- a/src/App/Controls/ExtendedTableView.cs
+++ b/src/App/Controls/ExtendedTableView.cs
@@ -38,6 +38,8 @@ namespace Bit.App.Controls
         }
 
         public int EstimatedRowHeight { get; set; }
+        public bool NoHeader { get; set; }
+        public bool NoFooter { get; set; }
 
         protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
         {
diff --git a/src/App/Pages/HomePage.cs b/src/App/Pages/HomePage.cs
index 1753d7db8..21ac7a245 100644
--- a/src/App/Pages/HomePage.cs
+++ b/src/App/Pages/HomePage.cs
@@ -71,7 +71,7 @@ namespace Bit.App.Pages
             };
 
             Title = "bitwarden";
-            Content = buttonStackLayout;
+            Content = new ScrollView { Content = buttonStackLayout };
         }
 
         public async Task LoginAsync()
diff --git a/src/App/Pages/RegisterPage.cs b/src/App/Pages/RegisterPage.cs
index 4d0fe9646..379afc731 100644
--- a/src/App/Pages/RegisterPage.cs
+++ b/src/App/Pages/RegisterPage.cs
@@ -61,18 +61,16 @@ namespace Bit.App.Pages
 
             var passwordLabel = new Label
             {
+                Text = "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it.",
                 LineBreakMode = LineBreakMode.WordWrap,
                 FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)),
-                Style = (Style)Application.Current.Resources["text-muted"]
+                Style = (Style)Application.Current.Resources["text-muted"],
+                Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25)
             };
-            var fs = new FormattedString();
-            fs.Spans.Add(new Span { Text = "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is" });
-            fs.Spans.Add(new Span { Text = " no way", FontAttributes = FontAttributes.Bold });
-            fs.Spans.Add(new Span { Text = " to recover the password in the event that you forget it." });
-            passwordLabel.FormattedText = fs;
 
             var table2 = new FormTableView
             {
+                NoHeader = true,
                 Root = new TableRoot
                 {
                     new TableSection
@@ -88,40 +86,21 @@ namespace Bit.App.Pages
                 Text = "A master password hint can help you remember your password if you forget it.",
                 LineBreakMode = LineBreakMode.WordWrap,
                 FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)),
-                Style = (Style)Application.Current.Resources["text-muted"]
+                Style = (Style)Application.Current.Resources["text-muted"],
+                Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25)
             };
 
-            var layout = new RelativeLayout
+            var layout = new StackLayout
             {
-                Padding = new Thickness(0, 0, 0, 35)
+                Children = { table, passwordLabel, table2, hintLabel },
+                Spacing = 0
             };
-            layout.Children.Add(
-                table,
-                Constraint.Constant(0),
-                Constraint.Constant(0),
-                Constraint.RelativeToParent((parent) => { return parent.Width; })
-            );
-            layout.Children.Add(
-                passwordLabel,
-                Constraint.Constant(15),
-                Constraint.RelativeToView(table, (parent, sibling) => { return sibling.Y + sibling.Height - (this.IsPortrait() ? 45 : 25); }),
-                Constraint.RelativeToParent((parent) => { return parent.Width - 30; })
-            );
-            layout.Children.Add(
-                table2,
-                Constraint.Constant(0),
-                Constraint.RelativeToView(passwordLabel, (parent, sibling) => { return sibling.Y + sibling.Height - (this.IsPortrait() ? 15 : 10); }),
-                Constraint.RelativeToParent((parent) => { return parent.Width; })
-            );
-            layout.Children.Add(
-                hintLabel,
-                Constraint.Constant(15),
-                Constraint.RelativeToView(table2, (parent, sibling) => { return sibling.Y + sibling.Height - (this.IsPortrait() ? 45 : 25); }),
-                Constraint.RelativeToParent((parent) => { return parent.Width - 30; })
-            );
 
-            layout.LowerChild(table2);
-            layout.LowerChild(table);
+            layout.LayoutChanged += (sender, args) =>
+            {
+                passwordLabel.WidthRequest = layout.Bounds.Width - passwordLabel.Bounds.Left * 2;
+                hintLabel.WidthRequest = layout.Bounds.Width - hintLabel.Bounds.Left * 2;
+            };
 
             var scrollView = new ScrollView
             {
@@ -207,6 +186,7 @@ namespace Bit.App.Pages
                 HasUnevenRows = true;
                 EnableSelection = false;
                 VerticalOptions = LayoutOptions.Start;
+                NoFooter = true;
             }
         }
     }
diff --git a/src/App/Pages/Settings/SettingsEditFolderPage.cs b/src/App/Pages/Settings/SettingsEditFolderPage.cs
index b78f322fe..8686ca88e 100644
--- a/src/App/Pages/Settings/SettingsEditFolderPage.cs
+++ b/src/App/Pages/Settings/SettingsEditFolderPage.cs
@@ -37,6 +37,7 @@ namespace Bit.App.Pages
 
             var nameCell = new FormEntryCell(AppResources.Name);
             nameCell.Entry.Text = folder.Name.Decrypt();
+            nameCell.Tapped += NameCell_Tapped;
 
             var deleteCell = new ExtendedTextCell { Text = AppResources.Delete, TextColor = Color.Red };
             deleteCell.Tapped += DeleteCell_Tapped;
@@ -45,16 +46,18 @@ namespace Bit.App.Pages
             {
                 Intent = TableIntent.Settings,
                 EnableScrolling = false,
-                EnableSelection = false,
+                EnableSelection = true,
                 HasUnevenRows = true,
                 VerticalOptions = LayoutOptions.Start,
-                BackgroundColor = Color.Gray,
-                Margin = new Thickness(0, -1),
                 Root = new TableRoot
                 {
-                    new TableSection()
+                    new TableSection
                     {
                         nameCell
+                    },
+                    new TableSection
+                    {
+                        deleteCell
                     }
                 }
             };
@@ -65,23 +68,6 @@ namespace Bit.App.Pages
                 mainTable.EstimatedRowHeight = 70;
             }
 
-            var deleteTable = new ExtendedTableView
-            {
-                Intent = TableIntent.Settings,
-                EnableScrolling = false,
-                EnableSelection = true,
-                VerticalOptions = LayoutOptions.End,
-                BackgroundColor = Color.Yellow,
-                Margin = new Thickness(0, -1),
-                Root = new TableRoot
-                {
-                    new TableSection()
-                    {
-                        deleteCell
-                    }
-                }
-            };
-
             var saveToolBarItem = new ToolbarItem(AppResources.Save, null, async () =>
             {
                 if(!_connectivity.IsConnected)
@@ -108,7 +94,7 @@ namespace Bit.App.Pages
             }, ToolbarItemOrder.Default, 0);
 
             Title = "Edit Folder";
-            Content = new ScrollView { Content = new StackLayout { Children = { mainTable, deleteTable } } };
+            Content = mainTable;
             ToolbarItems.Add(saveToolBarItem);
             if(Device.OS == TargetPlatform.iOS)
             {
@@ -121,6 +107,15 @@ namespace Bit.App.Pages
             }
         }
 
+        private void NameCell_Tapped(object sender, EventArgs e)
+        {
+            var cell = sender as FormEntryCell;
+            if(cell != null)
+            {
+                cell.Entry.Focus();
+            }
+        }
+
         private async void DeleteCell_Tapped(object sender, EventArgs e)
         {
             // TODO: Validate the delete operation. ex. Cannot delete a folder that has sites in it?
diff --git a/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs b/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs
index 35ad9d460..5cf5a7509 100644
--- a/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs
+++ b/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs
@@ -63,9 +63,9 @@ namespace Bit.App.Pages
             var table = new ExtendedTableView
             {
                 EnableScrolling = false,
-                Intent = TableIntent.Menu,
+                Intent = TableIntent.Settings,
                 HasUnevenRows = true,
-                VerticalOptions = LayoutOptions.End,
+                NoFooter = true,
                 Root = new TableRoot
                 {
                     new TableSection
@@ -95,13 +95,16 @@ namespace Bit.App.Pages
             var stackLayout = new StackLayout
             {
                 Orientation = StackOrientation.Vertical,
-                Children = { Password, table }
+                Children = { Password, table },
+                VerticalOptions = LayoutOptions.FillAndExpand,
+                Spacing = 0
             };
 
             var scrollView = new ScrollView
             {
                 Content = stackLayout,
-                Orientation = ScrollOrientation.Vertical
+                Orientation = ScrollOrientation.Vertical,
+                VerticalOptions = LayoutOptions.FillAndExpand
             };
 
             Title = "Generate Password";
diff --git a/src/iOS/Controls/ExtendedTableViewRenderer.cs b/src/iOS/Controls/ExtendedTableViewRenderer.cs
index 9011b5d14..e184e8403 100644
--- a/src/iOS/Controls/ExtendedTableViewRenderer.cs
+++ b/src/iOS/Controls/ExtendedTableViewRenderer.cs
@@ -3,6 +3,7 @@ using System.ComponentModel;
 using Bit.App.Controls;
 using Bit.iOS.Controls;
 using CoreGraphics;
+using Foundation;
 using UIKit;
 using Xamarin.Forms;
 using Xamarin.Forms.Platform.iOS;
@@ -24,8 +25,20 @@ namespace Bit.iOS.Controls
                 UpdateRowHeight(view);
                 UpdateEstimatedRowHeight(view);
                 UpdateSeparatorColor(view);
+
+                if(view.NoFooter)
+                {
+                    Control.SectionFooterHeight = 0.00001f;
+                }
+                if(view.NoHeader)
+                {
+                    Control.SectionHeaderHeight = 0.00001f;
+                }
+
+                SetSource();
             }
         }
+
         protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
         {
             base.OnElementPropertyChanged(sender, e);
@@ -44,6 +57,15 @@ namespace Bit.iOS.Controls
             {
                 SetSelection(view);
             }
+            else if(e.PropertyName == TableView.HasUnevenRowsProperty.PropertyName)
+            {
+                SetSource();
+            }
+        }
+
+        private void SetSource()
+        {
+            Control.Source = new CustomTableViewModelRenderer((ExtendedTableView)Element);
         }
 
         private void SetScrolling(ExtendedTableView view)
@@ -85,5 +107,70 @@ namespace Bit.iOS.Controls
         {
             Control.SeparatorColor = view.SeparatorColor.ToUIColor(UIColor.Gray);
         }
+
+        public class CustomTableViewModelRenderer : UnEvenTableViewModelRenderer
+        {
+            private readonly ExtendedTableView _view;
+
+            public CustomTableViewModelRenderer(ExtendedTableView model)
+                : base(model)
+            {
+                _view = model;
+            }
+
+            public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
+            {
+                if(_view.HasUnevenRows)
+                {
+                    return UITableView.AutomaticDimension;
+                }
+
+                return base.GetHeightForRow(tableView, indexPath);
+            }
+
+            public override nfloat GetHeightForHeader(UITableView tableView, nint section)
+            {
+                if(_view.NoHeader)
+                {
+                    return 0.00001f;
+                }
+
+                return base.GetHeightForHeader(tableView, section);
+            }
+
+            public override UIView GetViewForHeader(UITableView tableView, nint section)
+            {
+                if(_view.NoHeader)
+                {
+                    return new UIView(CGRect.Empty);
+                }
+
+                return base.GetViewForHeader(tableView, section);
+            }
+
+            public override nfloat GetHeightForFooter(UITableView tableView, nint section)
+            {
+                if(_view.NoFooter)
+                {
+                    return 0.00001f;
+                }
+
+                return 1f;
+            }
+
+            public override UIView GetViewForFooter(UITableView tableView, nint section)
+            {
+                if(_view.NoFooter)
+                {
+                    var view = new UIView(CGRect.Empty)
+                    {
+                        Hidden = true
+                    };
+                    return view;
+                }
+
+                return null;
+            }
+        }
     }
 }