category management

This commit is contained in:
2022-03-07 17:34:09 +08:00
parent 49e4e46cdb
commit 46464e19dc
131 changed files with 3992 additions and 189 deletions

View File

@ -9,7 +9,8 @@
x:Name="accountPage"
x:DataType="v:AccountPage"
Title="{r:Text Accounts}"
BindingContext="{x:Reference accountPage}">
BindingContext="{x:Reference accountPage}"
Shell.TabBarIsVisible="True">
<ContentPage.Resources>
<ui:MoneyConverter x:Key="moneyConverter"/>

View File

@ -177,22 +177,16 @@ namespace Billing.Views
}
using (Tap.Start())
{
var source = App.Categories.Select(c => new SelectItem<int>
{
Value = c.Id,
Name = c.Name,
Icon = c.Icon
});
var page = new ItemSelectPage<SelectItem<int>>(source);
page.ItemTapped += Category_ItemTapped;
var page = new CategorySelectPage(categoryId);
page.CategoryTapped += CategorySelectPage_Tapped;
await Navigation.PushAsync(page);
}
}
private void Category_ItemTapped(object sender, SelectItem<int> category)
private void CategorySelectPage_Tapped(object sender, UICategory e)
{
categoryId = category.Value;
SetValue(CategoryNameProperty, category.Name);
categoryId = e.Category.Id;
SetValue(CategoryNameProperty, e.Name);
}
private async void OnSelectWallet()

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:r="clr-namespace:Billing.Languages"
xmlns:ui="clr-namespace:Billing.UI"
xmlns:v="clr-namespace:Billing.Views"
x:Class="Billing.Views.AddCategoryPage"
x:Name="addCategoryPage"
x:DataType="v:AddCategoryPage"
BindingContext="{x:Reference addCategoryPage}">
<ContentPage.Resources>
<ui:IconConverter x:Key="iconConverter"/>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Order="Primary" IconImageSource="check.png" Command="{Binding CheckCategory}"/>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<TableView Intent="Settings" HasUnevenRows="True">
<TableSection Title=" ">
<ui:OptionEditorCell Height="120" Icon="pencil.png" FontSize="20" Keyboard="Text"
Title="{r:Text Name}"
Text="{Binding CategoryName, Mode=TwoWay}"
Placeholder="{r:Text NamePlaceholder}"/>
<ui:OptionImageCell Height="44" Icon="face.png"
Title="{r:Text Icon}"
ImageSource="{Binding CategoryIcon, Converter={StaticResource iconConverter}}"
TintColor="{Binding TintColor}"
Command="{Binding SelectIcon}"/>
</TableSection>
<TableSection>
<TableSection.Title>
<OnPlatform x:TypeArguments="x:String" Android=" "/>
</TableSection.Title>
<ui:OptionEntryCell Height="44" Icon="color.png" Keyboard="Numeric"
Title="{r:Text PrimaryColor}"
Text="{Binding TintColorString, Mode=TwoWay}"/>
<ViewCell Height="120">
<Grid BackgroundColor="{DynamicResource OptionTintColor}"
ColumnDefinitions=".3*, .7*" Padding="10">
<ui:ColorPicker Grid.Column="1" ColorChanged="ColorPicker_ColorChanged"/>
</Grid>
</ViewCell>
</TableSection>
</TableView>
</ContentPage.Content>
</ui:BillingPage>

View File

@ -0,0 +1,137 @@
using Billing.Languages;
using Billing.Models;
using Billing.Themes;
using Billing.UI;
using System;
using System.Linq;
using Xamarin.Forms;
namespace Billing.Views
{
public partial class AddCategoryPage : BillingPage
{
private static readonly BindableProperty CategoryNameProperty = BindableProperty.Create(nameof(CategoryName), typeof(string), typeof(AddCategoryPage));
private static readonly BindableProperty CategoryIconProperty = BindableProperty.Create(nameof(CategoryIcon), typeof(string), typeof(AddCategoryPage));
private static readonly BindableProperty TintColorProperty = BindableProperty.Create(nameof(TintColor), typeof(Color), typeof(AddCategoryPage));
private static readonly BindableProperty TintColorStringProperty = BindableProperty.Create(nameof(TintColorString), typeof(string), typeof(AddCategoryPage));
public string CategoryName
{
get => (string)GetValue(CategoryNameProperty);
set => SetValue(CategoryNameProperty, value);
}
public string CategoryIcon
{
get => (string)GetValue(CategoryIconProperty);
set => SetValue(CategoryIconProperty, value);
}
public Color TintColor
{
get => (Color)GetValue(TintColorProperty);
set => SetValue(TintColorProperty, value);
}
public string TintColorString
{
get => (string)GetValue(TintColorStringProperty);
set => SetValue(TintColorStringProperty, value);
}
public Command CheckCategory { get; }
public Command SelectIcon { get; }
public event EventHandler<Category> CategoryChecked;
private readonly int categoryId;
private readonly Category parent;
public AddCategoryPage(int id = -1, Category parent = null)
{
categoryId = id;
this.parent = parent;
var category = App.Categories.FirstOrDefault(c => c.Id == id);
Title = category?.Name ?? Resource.AddCategory;
if (category != null)
{
CategoryName = category.Name;
CategoryIcon = category.Icon;
if (category.TintColor == Color.Transparent ||
category.TintColor == default)
{
TintColor = (Color)Application.Current.Resources[BaseTheme.PrimaryColor];
}
else
{
TintColor = category.TintColor;
}
}
else
{
TintColor = (Color)Application.Current.Resources[BaseTheme.PrimaryColor];
}
TintColorString = Helper.WrapColorString(TintColor.ToHex());
CheckCategory = new Command(OnCheckCategory);
SelectIcon = new Command(OnSelectIcon);
InitializeComponent();
}
private void ColorPicker_ColorChanged(object sender, Color e)
{
TintColor = e;
TintColorString = Helper.WrapColorString(e.ToHex());
}
private async void OnCheckCategory()
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
var category = App.Categories.FirstOrDefault(c => c.Id == categoryId);
if (category == null)
{
CategoryChecked?.Invoke(this, new Category
{
Id = -1,
Name = CategoryName,
Icon = CategoryIcon,
TintColor = TintColor,
ParentId = parent?.Id ?? -1,
Type = parent?.Type ?? CategoryType.Spending
});
}
else
{
category.Name = CategoryName;
category.Icon = CategoryIcon;
category.TintColor = TintColor;
CategoryChecked?.Invoke(this, category);
}
await Navigation.PopAsync();
}
}
private async void OnSelectIcon()
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
var page = new IconSelectPage(CategoryIcon);
page.IconChecked += Category_IconChecked;
await Navigation.PushAsync(page);
}
}
private void Category_IconChecked(object sender, string icon)
{
CategoryIcon = icon;
}
}
}

View File

@ -8,7 +8,8 @@
x:DataType="v:BillPage"
x:Name="billPage"
BindingContext="{x:Reference billPage}"
Title="{r:Text Bills}">
Title="{r:Text Bills}"
Shell.TabBarIsVisible="True">
<ContentPage.Resources>
<ui:TitleDateConverter x:Key="titleDateConverter"/>

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms;
using Resource = Billing.Languages.Resource;
namespace Billing.Views
{
@ -170,8 +171,9 @@ namespace Billing.Views
}
e.Id = maxId + 1;
App.Bills.Add(e);
Bills.Add(WrapBill(e));
billsLayout.Refresh(Bills);
var bills = Bills;
bills.Add(WrapBill(e));
Bills = bills.OrderBy(b => b.DateCreation).ToList();
}
else
{

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8" ?>
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:ui="clr-namespace:Billing.UI"
xmlns:v="clr-namespace:Billing.Views"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Billing.Views.CategoryPage"
x:Name="categoryPage"
x:DataType="v:CategoryPage"
BindingContext="{x:Reference categoryPage}">
<ContentPage.Resources>
<ui:IconConverter x:Key="iconConverter"/>
</ContentPage.Resources>
<ScrollView>
<ui:GroupStackLayout x:Name="groupLayout" ItemsSource="{Binding Categories}" Margin="0, 10, 0, 0"
GroupHeight="36" RowHeight="44">
<ui:GroupStackLayout.GroupHeaderTemplate>
<DataTemplate x:DataType="v:CategoryGrouping">
<StackLayout Orientation="Horizontal" Padding="10, 0" VerticalOptions="End">
<Label Text="{Binding Key}" TextColor="{DynamicResource SecondaryTextColor}"/>
</StackLayout>
</DataTemplate>
</ui:GroupStackLayout.GroupHeaderTemplate>
<ui:GroupStackLayout.ItemTemplate>
<DataTemplate x:DataType="v:UICategory">
<ui:LongPressGrid Padding="20, 0" ColumnSpacing="10" ColumnDefinitions="Auto, *, Auto"
LongCommand="{Binding LongPressed, Source={x:Reference categoryPage}}"
LongCommandParameter="{Binding .}">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Tapped, Source={x:Reference categoryPage}}"
CommandParameter="{Binding .}"/>
</Grid.GestureRecognizers>
<ui:TintImage Source="{Binding Icon, Converter={StaticResource iconConverter}}"
PrimaryColor="{Binding TintColor}"
WidthRequest="26" HeightRequest="20" VerticalOptions="Center"/>
<Label Grid.Column="1" Text="{Binding Name}" TextColor="{DynamicResource TextColor}"
HorizontalOptions="FillAndExpand" VerticalOptions="Center"
FontSize="Default" FontAttributes="Bold"/>
<ui:TintImage Grid.Column="2" Source="right.png" HeightRequest="20"
IsVisible="{Binding IsTopCategory}"/>
</ui:LongPressGrid>
</DataTemplate>
</ui:GroupStackLayout.ItemTemplate>
</ui:GroupStackLayout>
</ScrollView>
</ui:BillingPage>

View File

@ -0,0 +1,247 @@
using Billing.Languages;
using Billing.Models;
using Billing.Themes;
using Billing.UI;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace Billing.Views
{
public partial class CategoryPage : BillingPage
{
private static readonly BindableProperty CategoriesProperty = BindableProperty.Create(nameof(Categories), typeof(IList), typeof(CategoryPage));
private static readonly BindableProperty IsTopCategoryProperty = BindableProperty.Create(nameof(IsTopCategory), typeof(bool), typeof(CategoryPage));
public IList Categories
{
get => (IList)GetValue(CategoriesProperty);
set => SetValue(CategoriesProperty, value);
}
public bool IsTopCategory => (bool)GetValue(IsTopCategoryProperty);
public Command LongPressed { get; }
public Command Tapped { get; }
private readonly int parentId;
public CategoryPage(int pid = -1)
{
parentId = pid;
var category = App.Categories.FirstOrDefault(c => c.Id == pid);
Title = category?.Name ?? Resource.CategoryManage;
if (category != null)
{
SetValue(IsTopCategoryProperty, false);
Categories = App.Categories.Where(c => c.ParentId == pid).Select(c => WrapCategory(c)).ToList();
ToolbarItems.Add(new ToolbarItem
{
Order = ToolbarItemOrder.Primary,
IconImageSource = "plus.png",
Command = new Command(OnAddCategory)
});
}
else
{
SetValue(IsTopCategoryProperty, true);
Categories = new List<CategoryGrouping>
{
new(Resource.Spending, App.Categories.Where(c => c.Type == CategoryType.Spending && c.ParentId == null).Select(c => WrapCategory(c))),
new(Resource.Income, App.Categories.Where(c => c.Type == CategoryType.Income && c.ParentId == null).Select(c => WrapCategory(c)))
};
}
LongPressed = new Command(OnLongPressed);
Tapped = new Command(OnTapped);
InitializeComponent();
}
private UICategory WrapCategory(Category category)
{
return new UICategory(category)
{
Icon = category.Icon,
Name = category.Name,
IsTopCategory = IsTopCategory,
TintColor = category.TintColor == Color.Transparent || category.TintColor == default ?
(Color)Application.Current.Resources[BaseTheme.PrimaryColor] :
category.TintColor
};
}
private async void OnAddCategory()
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
var page = new AddCategoryPage();
page.CategoryChecked += OnCategoryChecked;
await Navigation.PushAsync(page);
}
}
private async void OnLongPressed(object o)
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
if (o is UICategory c)
{
if (parentId < 0)
{
var page = new AddCategoryPage(c.Category.Id);
page.CategoryChecked += OnCategoryChecked;
await Navigation.PushAsync(page);
}
else
{
var result = await this.ShowConfirm(string.Format(Resource.ConfirmDeleteCategory, c.Category.Name));
if (result)
{
Categories.Remove(c);
groupLayout.Refresh(Categories);
App.Categories.Remove(c.Category);
_ = Task.Run(App.WriteCategories);
}
}
}
}
}
private async void OnTapped(object o)
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
if (o is UICategory c)
{
if (parentId < 0)
{
var page = new CategoryPage(c.Category.Id);
await Navigation.PushAsync(page);
}
else
{
var page = new AddCategoryPage(c.Category.Id);
page.CategoryChecked += OnCategoryChecked;
await Navigation.PushAsync(page);
}
}
}
}
private void OnCategoryChecked(object sender, Category category)
{
if (category.Id < 0)
{
// add
int maxId;
if (App.Categories.Count > 0)
{
maxId = App.Categories.Max(b => b.Id);
}
else
{
maxId = -1;
}
category.Id = maxId + 1;
App.Categories.Add(category);
Categories.Add(WrapCategory(category));
}
else
{
foreach (var o in Categories)
{
if (o is CategoryGrouping grouping)
{
var c = grouping.FirstOrDefault(c => c.Category == category);
if (c != null)
{
UpdateCategory(c);
}
}
else if (o is UICategory c && c.Category == category)
{
UpdateCategory(c);
break;
}
}
}
groupLayout.Refresh(Categories);
Task.Run(App.WriteCategories);
}
private void UpdateCategory(UICategory c)
{
c.Name = c.Category.Name;
c.Icon = c.Category.Icon;
c.TintColor = c.Category.TintColor;
}
}
public class UICategory : BindableObject
{
public static readonly BindableProperty IsCheckedProperty = BindableProperty.Create(nameof(IsChecked), typeof(bool), typeof(UICategory));
public static readonly BindableProperty IconProperty = BindableProperty.Create(nameof(Icon), typeof(string), typeof(UICategory));
public static readonly BindableProperty NameProperty = BindableProperty.Create(nameof(Name), typeof(string), typeof(UICategory));
public static readonly BindableProperty TintColorProperty = BindableProperty.Create(nameof(TintColor), typeof(Color), typeof(UICategory));
public static readonly BindableProperty IsTopCategoryProperty = BindableProperty.Create(nameof(IsTopCategory), typeof(bool), typeof(UICategory));
public bool IsChecked
{
get => (bool)GetValue(IsCheckedProperty);
set => SetValue(IsCheckedProperty, value);
}
public string Icon
{
get => (string)GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
public string Name
{
get => (string)GetValue(NameProperty);
set => SetValue(NameProperty, value);
}
public Color TintColor
{
get => (Color)GetValue(TintColorProperty);
set => SetValue(TintColorProperty, value);
}
public bool IsTopCategory
{
get => (bool)GetValue(IsTopCategoryProperty);
set => SetValue(IsTopCategoryProperty, value);
}
public Category Category { get; }
public UICategory(Category category)
{
Category = category;
}
}
public class CategoryGrouping : List<UICategory>, IGrouping<string, UICategory>
{
public string Key { get; }
public CategoryGrouping(string key, IEnumerable<UICategory> categories) : base(categories)
{
Key = key;
}
}
}

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8" ?>
<ui:BillingPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:r="clr-namespace:Billing.Languages"
xmlns:ui="clr-namespace:Billing.UI"
xmlns:v="clr-namespace:Billing.Views"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Billing.Views.CategorySelectPage"
x:Name="categorySelectPage"
x:DataType="v:CategorySelectPage"
BindingContext="{x:Reference categorySelectPage}"
Title="{r:Text SelectCategory}">
<ContentPage.Resources>
<ui:IconConverter x:Key="iconConverter"/>
<ui:SelectBackgroundColorConverter x:Key="backgroundConverter"/>
</ContentPage.Resources>
<Grid ColumnDefinitions=".5*, .5*">
<ScrollView>
<ui:GroupStackLayout ItemsSource="{Binding TopCategories}" Margin="0, 10, 0, 0"
GroupHeight="36" RowHeight="44">
<ui:GroupStackLayout.GroupHeaderTemplate>
<DataTemplate x:DataType="v:CategoryGrouping">
<StackLayout Orientation="Horizontal" Padding="10, 0" VerticalOptions="End">
<Label Text="{Binding Key}"
TextColor="{DynamicResource SecondaryTextColor}"/>
</StackLayout>
</DataTemplate>
</ui:GroupStackLayout.GroupHeaderTemplate>
<ui:GroupStackLayout.ItemTemplate>
<DataTemplate x:DataType="v:UICategory">
<Grid Padding="20, 0, 10, 0" ColumnSpacing="10" ColumnDefinitions="Auto, *"
BackgroundColor="{Binding IsChecked, Converter={StaticResource backgroundConverter}}">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapTopCategory, Source={x:Reference categorySelectPage}}"
CommandParameter="{Binding .}"/>
</Grid.GestureRecognizers>
<ui:TintImage Source="{Binding Icon, Converter={StaticResource iconConverter}}"
PrimaryColor="{Binding TintColor}"
WidthRequest="26" HeightRequest="20" VerticalOptions="Center"/>
<Label Grid.Column="1" Text="{Binding Name}" TextColor="{DynamicResource TextColor}"
HorizontalOptions="FillAndExpand" VerticalOptions="Center"
FontSize="Default" FontAttributes="Bold"/>
</Grid>
</DataTemplate>
</ui:GroupStackLayout.ItemTemplate>
</ui:GroupStackLayout>
</ScrollView>
<ScrollView Grid.Column="1">
<ui:GroupStackLayout ItemsSource="{Binding SubCategories}" Margin="0, 10, 0, 0" RowHeight="44" Padding="0, 36, 0, 0">
<ui:GroupStackLayout.ItemTemplate>
<DataTemplate x:DataType="v:UICategory">
<Grid Padding="20, 0, 10, 0" ColumnSpacing="10" ColumnDefinitions="Auto, *"
BackgroundColor="{Binding IsChecked, Converter={StaticResource backgroundConverter}}">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapSubCategory, Source={x:Reference categorySelectPage}}"
CommandParameter="{Binding .}"/>
</Grid.GestureRecognizers>
<ui:TintImage Source="{Binding Icon, Converter={StaticResource iconConverter}}"
PrimaryColor="{Binding TintColor}"
WidthRequest="26" HeightRequest="20" VerticalOptions="Center"/>
<Label Grid.Column="1" Text="{Binding Name}" TextColor="{DynamicResource TextColor}"
HorizontalOptions="FillAndExpand" VerticalOptions="Center"
FontSize="Default" FontAttributes="Bold"/>
</Grid>
</DataTemplate>
</ui:GroupStackLayout.ItemTemplate>
</ui:GroupStackLayout>
</ScrollView>
</Grid>
</ui:BillingPage>

View File

@ -0,0 +1,120 @@
using Billing.Languages;
using Billing.Models;
using Billing.Themes;
using Billing.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;
namespace Billing.Views
{
public partial class CategorySelectPage : BillingPage
{
private static readonly BindableProperty TopCategoriesProperty = BindableProperty.Create(nameof(TopCategories), typeof(List<CategoryGrouping>), typeof(CategorySelectPage));
private static readonly BindableProperty SubCategoriesProperty = BindableProperty.Create(nameof(SubCategories), typeof(List<UICategory>), typeof(CategorySelectPage));
public List<CategoryGrouping> TopCategories
{
get => (List<CategoryGrouping>)GetValue(TopCategoriesProperty);
set => SetValue(TopCategoriesProperty, value);
}
public List<UICategory> SubCategories
{
get => (List<UICategory>)GetValue(SubCategoriesProperty);
set => SetValue(SubCategoriesProperty, value);
}
public Command TapTopCategory { get; }
public Command TapSubCategory { get; }
public event EventHandler<UICategory> CategoryTapped;
private readonly int categoryId;
private readonly Color defaultColor;
public CategorySelectPage(int id)
{
categoryId = id;
defaultColor = (Color)Application.Current.Resources[BaseTheme.PrimaryColor];
TapTopCategory = new Command(OnTopCategoryTapped);
TapSubCategory = new Command(OnSubCategoryTapped);
TopCategories = new List<CategoryGrouping>
{
new(Resource.Spending, App.Categories.Where(c => c.Type == CategoryType.Spending && c.ParentId == null).Select(c => WrapCategory(c))),
new(Resource.Income, App.Categories.Where(c => c.Type == CategoryType.Income && c.ParentId == null).Select(c => WrapCategory(c)))
};
UICategory cat;
var category = App.Categories.FirstOrDefault(c => c.Id == categoryId);
if (category == null)
{
cat = TopCategories.Where(g => g.Count > 0).Select(g => g.First()).FirstOrDefault();
}
else if (category.ParentId == null)
{
cat = TopCategories.SelectMany(g => g).FirstOrDefault(c => c.Category == category);
}
else
{
cat = TopCategories.SelectMany(g => g).FirstOrDefault(c => c.Category.Id == category.ParentId);
}
OnTopCategoryTapped(cat);
InitializeComponent();
}
private UICategory WrapCategory(Category c)
{
return new UICategory(c)
{
IsChecked = c.Id == categoryId,
Icon = c.Icon,
Name = c.Name,
TintColor = c.TintColor == Color.Transparent || c.TintColor == default ? defaultColor : c.TintColor
};
}
private async void OnTopCategoryTapped(object o)
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
if (o is UICategory category)
{
var many = TopCategories.SelectMany(g => g);
foreach (var m in many)
{
m.IsChecked = m == category;
}
SubCategories = App.Categories.Where(c => c.ParentId == category.Category.Id).Select(c => WrapCategory(c)).ToList();
if (SubCategories.Count == 0)
{
CategoryTapped?.Invoke(this, category);
await Navigation.PopAsync();
}
}
}
}
private async void OnSubCategoryTapped(object o)
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
if (o is UICategory category)
{
CategoryTapped?.Invoke(this, category);
await Navigation.PopAsync();
}
}
}
}
}

View File

@ -39,12 +39,34 @@ namespace Billing.Views
{
new() { Icon = BaseModel.ICON_DEFAULT },
new() { Icon = "wallet" },
new() { Icon = "dollar" },
new() { Icon = "creditcard" },
new() { Icon = "debitcard" },
new() { Icon = "cmb" },
new() { Icon = "rcb" },
new() { Icon = "yuebao" },
new() { Icon = "zhaozhaoying" }
new() { Icon = "zhaozhaoying" },
new() { Icon = "clothes" },
new() { Icon = "food" },
new() { Icon = "drink" },
new() { Icon = "daily" },
new() { Icon = "trans" },
new() { Icon = "face" },
new() { Icon = "learn" },
new() { Icon = "medical" },
new() { Icon = "gem" },
new() { Icon = "makeup" },
new() { Icon = "brunch" },
new() { Icon = "dinner" },
new() { Icon = "fruit" },
new() { Icon = "bill" },
new() { Icon = "fee" },
new() { Icon = "rent" },
new() { Icon = "maintenance" },
new() { Icon = "rail" },
new() { Icon = "taxi" },
new() { Icon = "fitness" },
new() { Icon = "party" },
};
source.AddRange(IconConverter.IconPreset.Select(icon => new BillingIcon { Icon = $"#brand#{icon.Key}" }));
foreach (var icon in source)
@ -59,15 +81,22 @@ namespace Billing.Views
private async void OnIconCheck(object o)
{
if (o is string icon)
if (Tap.IsBusy)
{
foreach (var ic in IconsSource)
return;
}
using (Tap.Start())
{
if (o is string icon)
{
ic.IsChecked = ic.Icon == icon;
foreach (var ic in IconsSource)
{
ic.IsChecked = ic.Icon == icon;
}
iconChecked = icon;
IconChecked?.Invoke(this, icon);
await Navigation.PopAsync();
}
iconChecked = icon;
IconChecked?.Invoke(this, icon);
await Navigation.PopAsync();
}
}
}

View File

@ -8,21 +8,32 @@
x:Name="settingPage"
x:DataType="v:SettingPage"
Title="{r:Text Settings}"
BindingContext="{x:Reference settingPage}">
BindingContext="{x:Reference settingPage}"
Shell.TabBarIsVisible="True">
<TableView Intent="Settings" RowHeight="36">
<TableView Intent="Settings" HasUnevenRows="True">
<TableSection Title="{r:Text About}">
<ui:OptionTextCell Title="{r:Text Version}"
<ui:OptionTextCell Height="36" Title="{r:Text Version}"
Detail="{Binding Version}"/>
</TableSection>
<TableSection Title="{r:Text Feature}">
<ui:OptionSelectCell Height="36" Title="{r:Text CategoryManage}"
Detail="{r:Text Detail}"
Command="{Binding CategoryCommand}"/>
</TableSection>
<TableSection Title="{r:Text Preference}">
<ui:OptionEntryCell Title="{r:Text PrimaryColor}"
Text="{Binding Red, Mode=TwoWay}"
Keyboard="Numeric"/>
<ui:OptionEntryCell Text="{Binding Green, Mode=TwoWay}"
Keyboard="Numeric"/>
<ui:OptionEntryCell Text="{Binding Blue, Mode=TwoWay}"
Keyboard="Numeric"/>
<ui:OptionEntryCell Height="36" Title="{r:Text PrimaryColor}"
Text="{Binding PrimaryColor, Mode=TwoWay}"
Keyboard="Text"/>
<ViewCell Height="120">
<Grid BackgroundColor="{DynamicResource OptionTintColor}"
ColumnDefinitions=".3*, .7*" Padding="10">
<!--<Label Text="" LineBreakMode="TailTruncation"
VerticalOptions="Center"
TextColor="{DynamicResource TextColor}"/>-->
<ui:ColorPicker Grid.Column="1" ColorChanged="ColorPicker_ColorChanged"/>
</Grid>
</ViewCell>
</TableSection>
</TableView>
</ui:BillingPage>

View File

@ -1,5 +1,6 @@
using Billing.Themes;
using Billing.UI;
using System.Globalization;
using Xamarin.Essentials;
using Xamarin.Forms;
@ -8,29 +9,20 @@ namespace Billing.Views
public partial class SettingPage : BillingPage
{
private static readonly BindableProperty VersionProperty = BindableProperty.Create(nameof(Version), typeof(string), typeof(SettingPage));
private static readonly BindableProperty RedProperty = BindableProperty.Create(nameof(Red), typeof(byte), typeof(SettingPage));
private static readonly BindableProperty GreenProperty = BindableProperty.Create(nameof(Green), typeof(byte), typeof(SettingPage));
private static readonly BindableProperty BlueProperty = BindableProperty.Create(nameof(Blue), typeof(byte), typeof(SettingPage));
private static readonly BindableProperty PrimaryColorProperty = BindableProperty.Create(nameof(PrimaryColor), typeof(string), typeof(SettingPage));
public string Version => (string)GetValue(VersionProperty);
public byte Red
public string PrimaryColor
{
get => (byte)GetValue(RedProperty);
set => SetValue(RedProperty, value);
}
public byte Green
{
get => (byte)GetValue(GreenProperty);
set => SetValue(GreenProperty, value);
}
public byte Blue
{
get => (byte)GetValue(BlueProperty);
set => SetValue(BlueProperty, value);
get => (string)GetValue(PrimaryColorProperty);
set => SetValue(PrimaryColorProperty, value);
}
public Command CategoryCommand { get; }
public SettingPage()
{
CategoryCommand = new Command(OnCategoryCommand);
InitializeComponent();
var (main, build) = Definition.GetVersion();
@ -42,20 +34,35 @@ namespace Billing.Views
base.OnAppearing();
//SetValue(VersionProperty, $"{AppInfo.VersionString} ({AppInfo.BuildString})");
var colorString = Preferences.Get(Definition.PrimaryColorKey, "#183153");
var color = Color.FromHex(colorString);
Red = (byte)(color.R * 255);
Green = (byte)(color.G * 255);
Blue = (byte)(color.B * 255);
var colorString = Preferences.Get(Definition.PrimaryColorKey, Helper.DEFAULT_COLOR);
PrimaryColor = Helper.WrapColorString(colorString);
}
protected override void OnDisappearing()
{
base.OnDisappearing();
var color = Color.FromRgb(Red, Green, Blue);
Preferences.Set(Definition.PrimaryColorKey, color.ToHex());
Light.Instance.RefreshColor(color);
var color = PrimaryColor;
Preferences.Set(Definition.PrimaryColorKey, color);
Light.Instance.RefreshColor(Color.FromHex(color));
}
private async void OnCategoryCommand()
{
if (Tap.IsBusy)
{
return;
}
using (Tap.Start())
{
var page = new CategoryPage();
await Navigation.PushAsync(page);
}
}
private void ColorPicker_ColorChanged(object sender, Color e)
{
PrimaryColor = Helper.WrapColorString(e.ToHex());
}
}
}