Split UI code into separate files per page

This commit is contained in:
2020-02-29 23:08:03 +01:00
parent 429bc5b76b
commit 0da3e87a5d
5 changed files with 374 additions and 209 deletions

View File

@@ -1,37 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EstusShots.Client;
using EstusShots.Gtk.Controls;
using EstusShots.Shared.Dto;
using EstusShots.Shared.Models.Parameters;
using Gtk;
using UI = Gtk.Builder.ObjectAttribute;
namespace EstusShots.Gtk
{
internal class MainWindow : Window
internal partial class MainWindow : Window
{
private const string ApiUrl = "http://localhost:5000/api/";
private const string ApplicationName = "Estus Shots";
private const string Version = "0.1";
private const int EpisodesPageNumber = 1;
[UI] private readonly Label _infoLabel = null;
[UI] public readonly Button LoadButton = null;
[UI] public readonly Label InfoLabel = null;
[UI] public readonly Box LoadingSpinner = null;
[UI] public readonly Button NewSeasonButton = null;
[UI] public readonly Button AddEpisodeButton = null;
[UI] public readonly Overlay SeasonsOverlay = null;
[UI] public readonly Overlay EpisodesOverlay = null;
[UI] public readonly TreeView SeasonsView = null;
[UI] public readonly TreeView EpisodesTreeView = null;
[UI] public readonly Notebook Navigation = null;
[UI] public readonly Box EpisodesPage = null;
private EstusShotsClient Client { get; }
private BindableListControl<Season> SeasonsControl { get; set; }
private BindableListControl<Episode> EpisodesControl { get; set; }
public MainWindow() : this(new Builder("MainWindow.glade"))
@@ -44,189 +30,19 @@ namespace EstusShots.Gtk
Client = new EstusShotsClient(ApiUrl);
DeleteEvent += Window_DeleteEvent;
LoadButton.Clicked += LoadButtonClicked;
NewSeasonButton.Clicked += NewSeasonButtonOnClicked;
AddEpisodeButton.Clicked += AddEpisodeButtonOnClicked;
InitSeasonsControl();
InitEpisodesControl();
// Call initialization code of each page
InitSeasonsPage();
InitEpisodesPage();
InitPlayersPage();
CreateEpisodesControl();
// The episodes page is not shown, as long as no season is selected
EpisodesPage.Hide();
Info("Application Started");
UpdateTitle();
// No need to wait for the loading to finnish
var _ = ReloadSeasons();
}
private void InitSeasonsControl()
{
var columns = new List<DataColumn>
{
new DataColumn(nameof(Season.DisplayName)) {Title = "Name"},
new DataColumn(nameof(Season.Description)) {Title = "Description"},
new DataColumn(nameof(Season.Start))
{
Title = "Start",
Format = date => (date as DateTime?)?.ToString("dd.MM.yyyy")
},
new DataColumn(nameof(Season.End))
{
Title = "End",
Format = date => (date as DateTime?)?.ToString("dd.MM.yyyy") ?? "Ongoing"
}
};
SeasonsControl = new BindableListControl<Season>(columns, nameof(Season.SeasonId), SeasonsView);
SeasonsControl.OnSelectionChanged += SeasonsViewOnOnSelectionChanged;
SeasonsControl.TreeView.RowActivated += SeasonsViewOnRowActivated;
}
private void SeasonsViewOnRowActivated(object o, RowActivatedArgs args)
{
UpdateTitle();
Navigation.Page = EpisodesPageNumber;
}
private void InitEpisodesControl()
{
var columns = new List<DataColumn>
{
new DataColumn(nameof(Episode.DisplayName)) {Title = "Name"},
new DataColumn(nameof(Episode.Title)) {Title = "Title"},
new DataColumn(nameof(Episode.Date))
{
Title = "Date",
Format = d => (d as DateTime?)?.ToString("dd.MM.yyyy")
},
new DataColumn(nameof(Episode.Start))
{
Title = "Start",
Format = d => (d as DateTime?)?.ToString("HH:mm")
},
new DataColumn(nameof(Episode.End))
{
Title = "End",
Format = d => (d as DateTime?)?.ToString("HH:mm") ?? "Ongoing"
}
};
EpisodesControl = new BindableListControl<Episode>(columns, nameof(Episode.EpisodeId), EpisodesTreeView);
}
private async void SeasonsViewOnOnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!(e.Selection is Season season)) return;
using var _ = new LoadingMode(this);
EpisodesPage.Show();
var parameter = new GetEpisodesParameter(season.SeasonId);
var res = await Client.Episodes.GetEpisodes(parameter);
EpisodesControl.Items = res.Data.Episodes;
EpisodesControl.DataBind();
Info($"{season.DisplayName}: {res.Data.Episodes.Count} episodes");
}
private async void NewSeasonButtonOnClicked(object sender, EventArgs e)
{
using var _ = new LoadingMode(this);
// TODO real season edit control
var season = new Season
{
Game = "Test Game",
Number = SeasonsControl.Items.Any() ? SeasonsControl.Items.Max(x => x.Number) + 1 : 1,
Start = DateTime.Now,
Description = "This is a demo description!"
};
var parameter = new SaveSeasonParameter(season);
var res = await Client.Seasons.SaveSeason(parameter);
if (!res.OperationResult.Success)
{
_infoLabel.Text = $"Error while creating Season: {res.OperationResult.ShortMessage}";
return;
}
await ReloadSeasons();
Info("Created new Season");
}
private async void AddEpisodeButtonOnClicked(object sender, EventArgs e)
{
if (SeasonsControl.SelectedItem == null) return;
using var _ = new LoadingMode(this);
var season = SeasonsControl.SelectedItem;
// Some random test data
var rand = new Random();
var start = new DateTime(
DateTime.Today.Year,
DateTime.Today.Month,
DateTime.Today.Day,
DateTime.Today.Hour + rand.Next(0, 12),
DateTime.Today.Minute + rand.Next(0, 59),
DateTime.Today.Second
);
var end = start.AddHours(rand.Next(2, 4));
var ep = new Episode
{
SeasonId = season.SeasonId,
Number = EpisodesControl.Items.Any() ? EpisodesControl.Items.Max(x => x.Number) + 1 : 1,
Title = $"An Episode in season '{season.Game}'",
Date = DateTime.Today,
Start = start,
End = end
};
var parameter = new SaveEpisodeParameter(ep);
var res = await Client.Episodes.SaveEpisode(parameter);
if (!res.OperationResult.Success)
{
Info($"Cannot add episode: {res.OperationResult.ShortMessage}");
return;
}
await ReloadEpisodes();
}
private async void LoadButtonClicked(object sender, EventArgs a)
{
using var _ = new LoadingMode(this);
Info("Loading Seasons...");
await ReloadSeasons();
}
private async Task ReloadSeasons()
{
var res = await Task.Factory.StartNew(
() => Client.Seasons.GetSeasons(new GetSeasonsParameter()).Result);
if (!res.OperationResult.Success)
{
_infoLabel.Text = $"Refresh Failed: {res.OperationResult.ShortMessage}";
return;
}
SeasonsControl.Items = res.Data.Seasons;
SeasonsControl.DataBind();
Info("Seasons Refreshed");
}
private async Task ReloadEpisodes()
{
var seasonId = SeasonsControl.SelectedItem.SeasonId;
var res = await Task.Factory.StartNew(
() => Client.Episodes.GetEpisodes(new GetEpisodesParameter(seasonId)).Result);
if (!res.OperationResult.Success)
{
_infoLabel.Text = $"Refresh Failed: {res.OperationResult.ShortMessage}";
return;
}
EpisodesControl.Items = res.Data.Episodes;
EpisodesControl.DataBind();
Info("Episodes Refreshed");
}
private void Window_DeleteEvent(object sender, DeleteEventArgs a)
@@ -236,13 +52,13 @@ namespace EstusShots.Gtk
private void Info(string message)
{
_infoLabel.Text = message;
InfoLabel.Text = message;
}
private void UpdateTitle()
{
Title = SeasonsControl.SelectedItem == null
? $"{ApplicationName} v{Version}"
Title = SeasonsControl.SelectedItem == null
? $"{ApplicationName} v{Version}"
: $"{SeasonsControl.SelectedItem.DisplayName} - {ApplicationName} v{Version}";
}
}

View File

@@ -270,18 +270,117 @@
</packing>
</child>
<child>
<object class="GtkBox">
<object class="GtkBox" id="PlayersPage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkLabel">
<object class="GtkOverlay" id="PlayersOverlay">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Players go here</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="PlayersTreeView">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="label" translatable="yes">Edit Player</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="PlayerEditorContainer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<property name="layout_style">start</property>
<child>
<object class="GtkButton" id="SavePlayerButton">
<property name="label">gtk-save</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="DeletePlayerButton">
<property name="label">gtk-delete</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
@@ -289,9 +388,6 @@
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="position">3</property>
@@ -335,7 +431,7 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="_infoLabel">
<object class="GtkLabel" id="InfoLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EstusShots.Gtk.Controls;
using EstusShots.Shared.Dto;
using EstusShots.Shared.Models.Parameters;
using Gtk;
using UI = Gtk.Builder.ObjectAttribute;
namespace EstusShots.Gtk
{
internal partial class MainWindow
{
[UI] public readonly Box EpisodesPage = null;
[UI] public readonly Button AddEpisodeButton = null;
[UI] public readonly Overlay EpisodesOverlay = null;
[UI] public readonly TreeView EpisodesTreeView = null;
private void InitEpisodesPage()
{
AddEpisodeButton.Clicked += AddEpisodeButtonOnClicked;
}
// Eevents
private async void AddEpisodeButtonOnClicked(object sender, EventArgs e)
{
if (SeasonsControl.SelectedItem == null) return;
using var _ = new LoadingMode(this);
var season = SeasonsControl.SelectedItem;
// Some random test data
var rand = new Random();
var start = new DateTime(
DateTime.Today.Year,
DateTime.Today.Month,
DateTime.Today.Day,
DateTime.Today.Hour + rand.Next(0, 12),
DateTime.Today.Minute + rand.Next(0, 59),
DateTime.Today.Second
);
var end = start.AddHours(rand.Next(2, 4));
var ep = new Episode
{
SeasonId = season.SeasonId,
Number = EpisodesControl.Items.Any() ? EpisodesControl.Items.Max(x => x.Number) + 1 : 1,
Title = $"An Episode in season '{season.Game}'",
Date = DateTime.Today,
Start = start,
End = end
};
var parameter = new SaveEpisodeParameter(ep);
var res = await Client.Episodes.SaveEpisode(parameter);
if (!res.OperationResult.Success)
{
Info($"Cannot add episode: {res.OperationResult.ShortMessage}");
return;
}
await ReloadEpisodes();
}
// Private Methods
private async Task ReloadEpisodes()
{
var seasonId = SeasonsControl.SelectedItem.SeasonId;
var res = await Task.Factory.StartNew(
() => Client.Episodes.GetEpisodes(new GetEpisodesParameter(seasonId)).Result);
if (!res.OperationResult.Success)
{
InfoLabel.Text = $"Refresh Failed: {res.OperationResult.ShortMessage}";
return;
}
EpisodesControl.Items = res.Data.Episodes;
EpisodesControl.DataBind();
Info("Episodes Refreshed");
}
private void CreateEpisodesControl()
{
var columns = new List<DataColumn>
{
new DataColumn(nameof(Episode.DisplayName)) {Title = "Name"},
new DataColumn(nameof(Episode.Title)) {Title = "Title"},
new DataColumn(nameof(Episode.Date))
{
Title = "Date",
Format = d => (d as DateTime?)?.ToString("dd.MM.yyyy")
},
new DataColumn(nameof(Episode.Start))
{
Title = "Start",
Format = d => (d as DateTime?)?.ToString("HH:mm")
},
new DataColumn(nameof(Episode.End))
{
Title = "End",
Format = d => (d as DateTime?)?.ToString("HH:mm") ?? "Ongoing"
}
};
EpisodesControl = new BindableListControl<Episode>(columns, nameof(Episode.EpisodeId), EpisodesTreeView);
}
}
}

View File

@@ -0,0 +1,21 @@
using Gtk;
using UI = Gtk.Builder.ObjectAttribute;
namespace EstusShots.Gtk
{
internal partial class MainWindow
{
[UI] public readonly Box PlayersPage = null;
[UI] public readonly Overlay PlayersOverlay = null;
[UI] public readonly TreeView PlayersTreeView = null;
[UI] public readonly Button SavePlayerButton = null;
[UI] public readonly Button DeletePlayerButton = null;
[UI] public readonly Box PlayerEditorContainer = null;
private void InitPlayersPage()
{
}
}
}

View File

@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EstusShots.Gtk.Controls;
using EstusShots.Shared.Dto;
using EstusShots.Shared.Models.Parameters;
using Gtk;
using UI = Gtk.Builder.ObjectAttribute;
namespace EstusShots.Gtk
{
internal partial class MainWindow
{
[UI] public readonly Button LoadButton = null;
[UI] public readonly Button NewSeasonButton = null;
[UI] public readonly TreeView SeasonsView = null;
[UI] public readonly Overlay SeasonsOverlay = null;
private BindableListControl<Season> SeasonsControl { get; set; }
private void InitSeasonsPage()
{
CreateSeasonsControl();
LoadButton.Clicked += LoadButtonClicked;
NewSeasonButton.Clicked += NewSeasonButtonOnClicked;
// No need to wait for the loading to finnish
var _ = ReloadSeasons();
}
// Events
private async void LoadButtonClicked(object sender, EventArgs a)
{
using var _ = new LoadingMode(this);
Info("Loading Seasons...");
await ReloadSeasons();
}
private async void NewSeasonButtonOnClicked(object sender, EventArgs e)
{
using var _ = new LoadingMode(this);
// TODO real season edit control
var season = new Season
{
Game = "Test Game",
Number = SeasonsControl.Items.Any() ? SeasonsControl.Items.Max(x => x.Number) + 1 : 1,
Start = DateTime.Now,
Description = "This is a demo description!"
};
var parameter = new SaveSeasonParameter(season);
var res = await Client.Seasons.SaveSeason(parameter);
if (!res.OperationResult.Success)
{
InfoLabel.Text = $"Error while creating Season: {res.OperationResult.ShortMessage}";
return;
}
await ReloadSeasons();
Info("Created new Season");
}
private async void SeasonsControlOnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!(e.Selection is Season season)) return;
using var _ = new LoadingMode(this);
EpisodesPage.Show();
var parameter = new GetEpisodesParameter(season.SeasonId);
var res = await Client.Episodes.GetEpisodes(parameter);
EpisodesControl.Items = res.Data.Episodes;
EpisodesControl.DataBind();
UpdateTitle();
Navigation.Page = EpisodesPageNumber;
Info($"{season.DisplayName}: {res.Data.Episodes.Count} episodes");
}
// Private Methods
private async Task ReloadSeasons()
{
var res = await Task.Factory.StartNew(
() => Client.Seasons.GetSeasons(new GetSeasonsParameter()).Result);
if (!res.OperationResult.Success)
{
InfoLabel.Text = $"Refresh Failed: {res.OperationResult.ShortMessage}";
return;
}
SeasonsControl.Items = res.Data.Seasons;
SeasonsControl.DataBind();
Info("Seasons Refreshed");
}
private void CreateSeasonsControl()
{
var columns = new List<DataColumn>
{
new DataColumn(nameof(Season.DisplayName)) {Title = "Name"},
new DataColumn(nameof(Season.Description)) {Title = "Description"},
new DataColumn(nameof(Season.Start))
{
Title = "Start",
Format = date => (date as DateTime?)?.ToString("dd.MM.yyyy")
},
new DataColumn(nameof(Season.End))
{
Title = "End",
Format = date => (date as DateTime?)?.ToString("dd.MM.yyyy") ?? "Ongoing"
}
};
SeasonsControl = new BindableListControl<Season>(columns, nameof(Season.SeasonId), SeasonsView);
SeasonsControl.OnSelectionChanged += SeasonsControlOnSelectionChanged;
}
}
}