Creating and updating seasons with an editor.
This commit is contained in:
@@ -7,39 +7,22 @@ using DateTime = GLib.DateTime;
|
||||
|
||||
namespace EstusShots.Gtk.Controls
|
||||
{
|
||||
public class SelectionChangedEventArgs : EventArgs
|
||||
public class SelectionChangedEventArgs<T> : EventArgs
|
||||
{
|
||||
public SelectionChangedEventArgs(object selection)
|
||||
public SelectionChangedEventArgs(T selection)
|
||||
{
|
||||
Selection = selection;
|
||||
}
|
||||
|
||||
public object Selection { get; }
|
||||
public T Selection { get; }
|
||||
}
|
||||
|
||||
public delegate void SelectionChangedEventHandler(object o, SelectionChangedEventArgs args);
|
||||
public delegate void SelectionChangedEventHandler<T>(object o, SelectionChangedEventArgs<T> args);
|
||||
|
||||
public delegate void ItemActivatedEventHandler<T>(T item);
|
||||
|
||||
public class BindableListControl<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialize a new BindableListView with an existing TreeView Widget
|
||||
/// </summary>
|
||||
/// <param name="columns">The columns of the grid</param>
|
||||
/// <param name="keyField">Unique key field in the item sources type</param>
|
||||
/// <param name="treeView">An instance of an existing TreeView Widget</param>
|
||||
public BindableListControl(List<DataColumn> columns, string keyField, TreeView treeView = null)
|
||||
{
|
||||
TreeView = treeView ?? new TreeView();
|
||||
Columns = columns;
|
||||
KeyField = keyField;
|
||||
InitTreeViewColumns();
|
||||
InitListStore();
|
||||
TreeView.Model = ListStore;
|
||||
Items = new List<T>();
|
||||
|
||||
TreeView.RowActivated += TreeViewOnRowActivated;
|
||||
}
|
||||
|
||||
/// <summary> The GTK ListStore that is managed by this <see cref="BindableListControl{T}" />. </summary>
|
||||
public ListStore ListStore { get; internal set; }
|
||||
|
||||
@@ -61,7 +44,32 @@ namespace EstusShots.Gtk.Controls
|
||||
/// <summary>
|
||||
/// Event will be invoked when the selected item in the <see cref="TreeView" /> has changed.
|
||||
/// </summary>
|
||||
public event SelectionChangedEventHandler OnSelectionChanged;
|
||||
public event SelectionChangedEventHandler<T> SelectionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Will be invoked when a row in the view has ben acitvated (e.g. double clicked)
|
||||
/// </summary>
|
||||
public event ItemActivatedEventHandler<T> ItemActivated;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new BindableListView with an existing TreeView Widget
|
||||
/// </summary>
|
||||
/// <param name="columns">The columns of the grid</param>
|
||||
/// <param name="keyField">Unique key field in the item sources type</param>
|
||||
/// <param name="treeView">An instance of an existing TreeView Widget</param>
|
||||
public BindableListControl(List<DataColumn> columns, string keyField, TreeView treeView = null)
|
||||
{
|
||||
TreeView = treeView ?? new TreeView();
|
||||
Columns = columns;
|
||||
KeyField = keyField;
|
||||
InitTreeViewColumns();
|
||||
InitListStore();
|
||||
TreeView.Model = ListStore;
|
||||
Items = new List<T>();
|
||||
|
||||
TreeView.RowActivated += TreeViewOnRowActivated;
|
||||
TreeView.Selection.Changed += TreeViewSelectionOnChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set elements from the <see cref="Items" /> property in the <see cref="ListStore" />.
|
||||
@@ -99,8 +107,8 @@ namespace EstusShots.Gtk.Controls
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void TreeViewOnRowActivated(object o, RowActivatedArgs args)
|
||||
|
||||
private void TreeViewOnRowActivated(object o, RowActivatedArgs args)
|
||||
{
|
||||
if (!(o is TreeView tree)) return;
|
||||
var selection = tree.Selection;
|
||||
@@ -120,7 +128,29 @@ namespace EstusShots.Gtk.Controls
|
||||
}
|
||||
|
||||
SelectedItem = item;
|
||||
OnSelectionChanged?.Invoke(this, new SelectionChangedEventArgs(SelectedItem));
|
||||
ItemActivated?.Invoke(SelectedItem);
|
||||
}
|
||||
|
||||
private void TreeViewSelectionOnChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (!(sender is TreeSelection selection)) return;
|
||||
selection.GetSelected(out var model, out var iter);
|
||||
var key = model.GetValue(iter, 0);
|
||||
var item = Items.FirstOrDefault(x =>
|
||||
{
|
||||
var prop = x.GetType().GetProperty(KeyField);
|
||||
var value = prop?.GetValue(x);
|
||||
return value != null && value.Equals(key);
|
||||
});
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
Console.WriteLine($"No item for key '{key}' found in data store");
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedItem = item;
|
||||
SelectionChanged?.Invoke(selection, new SelectionChangedEventArgs<T>(SelectedItem));
|
||||
}
|
||||
|
||||
private void InitTreeViewColumns()
|
||||
@@ -130,10 +160,11 @@ namespace EstusShots.Gtk.Controls
|
||||
// Offset by one, because the first column in the data store is fixed to the key value of the row
|
||||
var valueIndex = Columns.IndexOf(dataColumn) + 1;
|
||||
dataColumn.AddAttribute(dataColumn.Cell, dataColumn.ValueAttribute, valueIndex);
|
||||
dataColumn.SortColumnId = valueIndex;
|
||||
TreeView.AppendColumn(dataColumn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void InitListStore()
|
||||
{
|
||||
var types = Columns
|
||||
|
||||
81
EstusShots.Gtk/Dialogs/DialogBase.cs
Normal file
81
EstusShots.Gtk/Dialogs/DialogBase.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using EstusShots.Shared.Models;
|
||||
using Gtk;
|
||||
using UI = Gtk.Builder.ObjectAttribute;
|
||||
|
||||
namespace EstusShots.Gtk.Dialogs
|
||||
{
|
||||
public class DialogClosedEventArgs<T> : EventArgs where T: class, new()
|
||||
{
|
||||
public bool Ok { get; }
|
||||
public T Model { get; }
|
||||
|
||||
public DialogClosedEventArgs(bool ok, T model)
|
||||
{
|
||||
Ok = ok;
|
||||
Model = model;
|
||||
}
|
||||
}
|
||||
public delegate void DialogClosedEventHandler<T>(object o, DialogClosedEventArgs<T> args) where T: class, new();
|
||||
|
||||
// TODO remove non-generic version
|
||||
public class DialogClosedEventArgs : EventArgs
|
||||
{
|
||||
public bool Ok { get; }
|
||||
public object Model { get; }
|
||||
|
||||
public DialogClosedEventArgs(bool ok, object model)
|
||||
{
|
||||
Ok = ok;
|
||||
Model = model;
|
||||
}
|
||||
}
|
||||
public delegate void DialogClosedEventHandler(object o, DialogClosedEventArgs args);
|
||||
|
||||
public abstract class DialogBase<T> where T: class, new()
|
||||
{
|
||||
protected T EditObject { get; set; }
|
||||
|
||||
[UI] private readonly Dialog _editorDialog = null;
|
||||
[UI] private readonly Button _saveButton = null;
|
||||
[UI] private readonly Button _cancelButton = null;
|
||||
|
||||
public event DialogClosedEventHandler<T> OnDialogClosed;
|
||||
|
||||
protected DialogBase(Window parent, Builder builder)
|
||||
{
|
||||
builder.Autoconnect(this);
|
||||
|
||||
_saveButton.Clicked += OnSaveButtonClicked;
|
||||
_cancelButton.Clicked += (sender, args) =>
|
||||
{
|
||||
OnDialogClosed?.Invoke(this, new DialogClosedEventArgs<T>(false, new T()));
|
||||
_editorDialog.Dispose();
|
||||
};
|
||||
_editorDialog.TransientFor = parent;
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
LoadFromModel();
|
||||
_editorDialog.Show();
|
||||
}
|
||||
|
||||
private void OnSaveButtonClicked(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadToModel();
|
||||
OnDialogClosed?.Invoke(this, new DialogClosedEventArgs<T>(true, EditObject));
|
||||
_editorDialog.Dispose();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
ErrorDialog.Show(new OperationResult(exception));
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void LoadToModel();
|
||||
protected abstract void LoadFromModel();
|
||||
}
|
||||
}
|
||||
210
EstusShots.Gtk/Dialogs/Glade/SeasonEditor.glade
Normal file
210
EstusShots.Gtk/Dialogs/Glade/SeasonEditor.glade
Normal file
@@ -0,0 +1,210 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<object class="GtkDialog" id="_editorDialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Season</property>
|
||||
<property name="resizable">False</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="default_width">400</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="gravity">center</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="_saveButton">
|
||||
<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>
|
||||
<accelerator key="Return" signal="clicked"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="_cancelButton">
|
||||
<property name="label">gtk-cancel</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>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkGrid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="row_spacing">5</property>
|
||||
<property name="column_spacing">7</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Number</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Game</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="_gameEntry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Start</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">End</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="label" translatable="yes">Description</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="min_content_height">150</property>
|
||||
<property name="propagate_natural_width">True</property>
|
||||
<property name="propagate_natural_height">True</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="_descriptionTextView">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="hscroll_policy">natural</property>
|
||||
<property name="vscroll_policy">natural</property>
|
||||
<property name="wrap_mode">word</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="_startEntry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="_endEntry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="secondary_icon_stock">gtk-clear</property>
|
||||
<property name="placeholder_text" translatable="yes">Ongoing</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="_numberEntry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="input_purpose">digits</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -5,19 +5,6 @@ using UI = Gtk.Builder.ObjectAttribute;
|
||||
|
||||
namespace EstusShots.Gtk.Dialogs
|
||||
{
|
||||
public class DialogClosedEventArgs : EventArgs
|
||||
{
|
||||
public bool Ok { get; }
|
||||
public object Model { get; }
|
||||
|
||||
public DialogClosedEventArgs(bool ok, object model)
|
||||
{
|
||||
Ok = ok;
|
||||
Model = model;
|
||||
}
|
||||
}
|
||||
public delegate void DialogClosedEventHandler(object o, DialogClosedEventArgs args);
|
||||
|
||||
public class PlayerEditor
|
||||
{
|
||||
private readonly Player _player;
|
||||
|
||||
57
EstusShots.Gtk/Dialogs/SeasonEditor.cs
Normal file
57
EstusShots.Gtk/Dialogs/SeasonEditor.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using EstusShots.Shared.Dto;
|
||||
using EstusShots.Shared.Extensions;
|
||||
using Gtk;
|
||||
using UI = Gtk.Builder.ObjectAttribute;
|
||||
|
||||
namespace EstusShots.Gtk.Dialogs
|
||||
{
|
||||
public class SeasonEditor : DialogBase<Season>
|
||||
{
|
||||
[UI] private readonly Entry _numberEntry = null;
|
||||
[UI] private readonly Entry _gameEntry = null;
|
||||
[UI] private readonly Entry _startEntry = null;
|
||||
[UI] private readonly Entry _endEntry = null;
|
||||
[UI] private readonly TextView _descriptionTextView = null;
|
||||
|
||||
public SeasonEditor(Window parent, Season season) : base(parent, new Builder("SeasonEditor.glade"))
|
||||
{
|
||||
EditObject = season;
|
||||
_startEntry.FocusOutEvent += (o, args) =>
|
||||
{
|
||||
if (!(o is Entry entry)) return;
|
||||
entry.Text = entry.Text.DateMask();
|
||||
};
|
||||
_endEntry.FocusOutEvent += (o, args) =>
|
||||
{
|
||||
if (!(o is Entry entry)) return;
|
||||
if (entry.Text.IsNullOrWhiteSpace()) return;
|
||||
entry.Text = entry.Text.DateMask();
|
||||
};
|
||||
_endEntry.IconPress += (o, args) =>
|
||||
{
|
||||
if (!(o is Entry entry)) return;
|
||||
entry.Text = "";
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadFromModel()
|
||||
{
|
||||
if (EditObject.SeasonId.IsEmpty()) return;
|
||||
_numberEntry.Text = EditObject.Number.ToString();
|
||||
_gameEntry.Text = EditObject.Game;
|
||||
_startEntry.Text = EditObject.Start.ToString("yyyy-MM-dd");
|
||||
_endEntry.Text = EditObject.End?.ToString("yyyy-MM-dd") ?? "";
|
||||
_descriptionTextView.Buffer = new TextBuffer(new TextTagTable()) {Text = EditObject.Description};
|
||||
}
|
||||
|
||||
protected override void LoadToModel()
|
||||
{
|
||||
EditObject.Number = _numberEntry.Text.ToInt32OrDefault();
|
||||
EditObject.Game = _gameEntry.Text;
|
||||
EditObject.Start = _startEntry.Text.ToDateTime();
|
||||
EditObject.End = _endEntry.Text.IsNullOrWhiteSpace() ? null :_endEntry.Text.ToNullableDateTime();
|
||||
EditObject.Description = _descriptionTextView.Buffer.Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,4 +25,8 @@
|
||||
<ProjectReference Include="..\EstusShots.Shared\EstusShots.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Dialogs\Glade" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -62,37 +62,6 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkOverlay" id="SeasonsOverlay">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</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="SeasonsView">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="enable_grid_lines">horizontal</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="GtkButtonBox">
|
||||
<property name="visible">True</property>
|
||||
@@ -132,7 +101,38 @@
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkOverlay" id="SeasonsOverlay">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</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="SeasonsView">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="enable_grid_lines">horizontal</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">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace EstusShots.Gtk
|
||||
new DataColumnText(nameof(Player.HexId)) {Title = "Hex ID"},
|
||||
};
|
||||
_playersControl = new BindableListControl<Player>(playerColumns, nameof(Player.PlayerId), PlayersTreeView);
|
||||
_playersControl.OnSelectionChanged += PlayersControlOnOnSelectionChanged;
|
||||
_playersControl.ItemActivated += PlayersControlActivated;
|
||||
|
||||
|
||||
var drinkColumns = new List<DataColumn>
|
||||
@@ -53,9 +53,8 @@ namespace EstusShots.Gtk
|
||||
|
||||
// Events
|
||||
|
||||
private void PlayersControlOnOnSelectionChanged(object o, SelectionChangedEventArgs args)
|
||||
private void PlayersControlActivated(Player player)
|
||||
{
|
||||
if (!(args.Selection is Player player)) return;
|
||||
var dialog = new PlayerEditor(this, player);
|
||||
dialog.OnDialogClosed += PlayerEditorClosed;
|
||||
}
|
||||
|
||||
@@ -40,18 +40,20 @@ namespace EstusShots.Gtk
|
||||
await ReloadSeasons();
|
||||
}
|
||||
|
||||
private async void NewSeasonButtonOnClicked(object sender, EventArgs e)
|
||||
private void NewSeasonButtonOnClicked(object sender, EventArgs e)
|
||||
{
|
||||
var dialog = new SeasonEditor(this, new Season());
|
||||
dialog.OnDialogClosed += SeasonEditorClosed;
|
||||
dialog.Show();
|
||||
}
|
||||
|
||||
private async void SeasonEditorClosed(object o, DialogClosedEventArgs<Season> args)
|
||||
{
|
||||
if (!args.Ok) return;
|
||||
|
||||
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 parameter = new SaveSeasonParameter(args.Model);
|
||||
var res = await Client.Seasons.SaveSeason(parameter);
|
||||
if (!res.OperationResult.Success)
|
||||
{
|
||||
@@ -61,24 +63,25 @@ namespace EstusShots.Gtk
|
||||
}
|
||||
|
||||
await ReloadSeasons();
|
||||
Info("Created new Season");
|
||||
Info($"Season {args.Model.DisplayName}");
|
||||
}
|
||||
|
||||
private async void SeasonsControlOnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
private async void SeasonsControlSelectionChanged(object sender, SelectionChangedEventArgs<Season> e)
|
||||
{
|
||||
if (!(e.Selection is Season season)) return;
|
||||
using var _ = new LoadingMode(this);
|
||||
|
||||
EpisodesPage.Show();
|
||||
var parameter = new GetEpisodesParameter(season.SeasonId);
|
||||
var parameter = new GetEpisodesParameter(e.Selection.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");
|
||||
Info($"{e.Selection.DisplayName}: {res.Data.Episodes.Count} episodes");
|
||||
}
|
||||
|
||||
private void SeasonsControlItemActivated(Season item)
|
||||
{
|
||||
var dialog = new SeasonEditor(this, item);
|
||||
dialog.OnDialogClosed += SeasonEditorClosed;
|
||||
dialog.Show();
|
||||
}
|
||||
|
||||
// Private Methods
|
||||
@@ -93,8 +96,9 @@ namespace EstusShots.Gtk
|
||||
ErrorDialog.Show(res.OperationResult);
|
||||
return;
|
||||
}
|
||||
|
||||
SeasonsControl.Items = res.Data.Seasons;
|
||||
|
||||
// TODO Initial ordering should be done by the control
|
||||
SeasonsControl.Items = res.Data.Seasons.OrderBy(x => x.DisplayName).ToList();
|
||||
SeasonsControl.DataBind();
|
||||
Info("Seasons Refreshed");
|
||||
}
|
||||
@@ -103,7 +107,7 @@ namespace EstusShots.Gtk
|
||||
{
|
||||
var columns = new List<DataColumn>
|
||||
{
|
||||
new DataColumnText(nameof(Season.DisplayName)) {Title = "Name"},
|
||||
new DataColumnText(nameof(Season.DisplayName)) {Title = "Name", SortOrder = SortType.Ascending},
|
||||
new DataColumnText(nameof(Season.Description)),
|
||||
new DataColumnText(nameof(Season.Start))
|
||||
{
|
||||
@@ -115,7 +119,8 @@ namespace EstusShots.Gtk
|
||||
}
|
||||
};
|
||||
SeasonsControl = new BindableListControl<Season>(columns, nameof(Season.SeasonId), SeasonsView);
|
||||
SeasonsControl.OnSelectionChanged += SeasonsControlOnSelectionChanged;
|
||||
SeasonsControl.SelectionChanged += SeasonsControlSelectionChanged;
|
||||
SeasonsControl.ItemActivated += SeasonsControlItemActivated;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,7 @@ namespace EstusShots.Server.Services
|
||||
_context.Players.Update(player);
|
||||
_mapper.Map(parameter.Player, player);
|
||||
var count = await _context.SaveChangesAsync();
|
||||
_logger.LogInformation($"Updated player '{player.PlayerId}'");
|
||||
_logger.LogInformation($"Updated player '{player.PlayerId}' ({count} rows)");
|
||||
return new ApiResponse<SavePlayerResponse>(new SavePlayerResponse(player.PlayerId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,18 +46,23 @@ namespace EstusShots.Server.Services
|
||||
|
||||
public async Task<ApiResponse<SaveSeasonResponse>> SaveSeason(SaveSeasonParameter parameter)
|
||||
{
|
||||
var season = _mapper.Map<Season>(parameter.Season);
|
||||
var existing = await _context.Seasons.FindAsync(season.SeasonId);
|
||||
if (existing == null)
|
||||
if (parameter.Season.SeasonId.IsEmpty())
|
||||
{
|
||||
_context.Seasons.Add(season);await _context.SaveChangesAsync();
|
||||
_logger.LogInformation($"New season created: '{season.SeasonId}'");
|
||||
var season = _mapper.Map<Season>(parameter.Season);
|
||||
_context.Seasons.Add(season);
|
||||
var count = await _context.SaveChangesAsync();
|
||||
_logger.LogInformation($"Season created: '{season.SeasonId}' ({count} rows updated)");
|
||||
return new ApiResponse<SaveSeasonResponse>(new SaveSeasonResponse(season.SeasonId));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var season = await _context.Seasons.FindAsync(parameter.Season.SeasonId);
|
||||
_context.Seasons.Update(season);
|
||||
_mapper.Map(parameter.Season, season);
|
||||
var count = await _context.SaveChangesAsync();
|
||||
_logger.LogInformation($"Season '{season.SeasonId}' updated ({count} rows updated)");
|
||||
return new ApiResponse<SaveSeasonResponse>(new SaveSeasonResponse(season.SeasonId));
|
||||
}
|
||||
return new ApiResponse<SaveSeasonResponse>(new SaveSeasonResponse(season.SeasonId));
|
||||
}
|
||||
}
|
||||
}
|
||||
38
EstusShots.Shared/Extensions/StringExtensions.cs
Normal file
38
EstusShots.Shared/Extensions/StringExtensions.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace EstusShots.Shared.Extensions
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Forces a string into the "yyyy-mm-dd" format
|
||||
/// </summary>
|
||||
/// <param name="this"></param>
|
||||
/// <returns></returns>
|
||||
public static string DateMask(this string @this)
|
||||
{
|
||||
// Remove all non-numbers
|
||||
@this = Regex.Replace(@this, "[^0-9.]", "");
|
||||
if (@this.Length < 8) @this += "????????";
|
||||
return string.Format("{0}-{1}-{2}",
|
||||
@this.Substring(0, 4), // The year,
|
||||
@this.Substring(4, 2), // The month,
|
||||
@this.Substring(6, 2)); // The day);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces a string into the "HH:MM" format
|
||||
/// </summary>
|
||||
/// <param name="this"></param>
|
||||
/// <returns></returns>
|
||||
public static string HourMinuteMask(this string @this)
|
||||
{
|
||||
// Remove all non-numbers
|
||||
@this = Regex.Replace(@this, "[^0-9.]", "");
|
||||
if (@this.Length < 4) @this += "0000";
|
||||
return string.Format("{0}:{1}",
|
||||
@this.Substring(0, 2), // The hours,
|
||||
@this.Substring(2, 4)); // The minutes
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user