Creating and updating seasons with an editor.
This commit is contained in:
@@ -7,39 +7,22 @@ using DateTime = GLib.DateTime;
|
|||||||
|
|
||||||
namespace EstusShots.Gtk.Controls
|
namespace EstusShots.Gtk.Controls
|
||||||
{
|
{
|
||||||
public class SelectionChangedEventArgs : EventArgs
|
public class SelectionChangedEventArgs<T> : EventArgs
|
||||||
{
|
{
|
||||||
public SelectionChangedEventArgs(object selection)
|
public SelectionChangedEventArgs(T selection)
|
||||||
{
|
{
|
||||||
Selection = 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>
|
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>
|
/// <summary> The GTK ListStore that is managed by this <see cref="BindableListControl{T}" />. </summary>
|
||||||
public ListStore ListStore { get; internal set; }
|
public ListStore ListStore { get; internal set; }
|
||||||
|
|
||||||
@@ -61,7 +44,32 @@ namespace EstusShots.Gtk.Controls
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event will be invoked when the selected item in the <see cref="TreeView" /> has changed.
|
/// Event will be invoked when the selected item in the <see cref="TreeView" /> has changed.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Set elements from the <see cref="Items" /> property in the <see cref="ListStore" />.
|
/// Set elements from the <see cref="Items" /> property in the <see cref="ListStore" />.
|
||||||
@@ -120,7 +128,29 @@ namespace EstusShots.Gtk.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
SelectedItem = item;
|
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()
|
private void InitTreeViewColumns()
|
||||||
@@ -130,6 +160,7 @@ namespace EstusShots.Gtk.Controls
|
|||||||
// Offset by one, because the first column in the data store is fixed to the key value of the row
|
// 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;
|
var valueIndex = Columns.IndexOf(dataColumn) + 1;
|
||||||
dataColumn.AddAttribute(dataColumn.Cell, dataColumn.ValueAttribute, valueIndex);
|
dataColumn.AddAttribute(dataColumn.Cell, dataColumn.ValueAttribute, valueIndex);
|
||||||
|
dataColumn.SortColumnId = valueIndex;
|
||||||
TreeView.AppendColumn(dataColumn);
|
TreeView.AppendColumn(dataColumn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
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
|
public class PlayerEditor
|
||||||
{
|
{
|
||||||
private readonly Player _player;
|
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" />
|
<ProjectReference Include="..\EstusShots.Shared\EstusShots.Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Dialogs\Glade" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -62,37 +62,6 @@
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="orientation">vertical</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>
|
<child>
|
||||||
<object class="GtkButtonBox">
|
<object class="GtkButtonBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@@ -132,7 +101,38 @@
|
|||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="padding">2</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>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace EstusShots.Gtk
|
|||||||
new DataColumnText(nameof(Player.HexId)) {Title = "Hex ID"},
|
new DataColumnText(nameof(Player.HexId)) {Title = "Hex ID"},
|
||||||
};
|
};
|
||||||
_playersControl = new BindableListControl<Player>(playerColumns, nameof(Player.PlayerId), PlayersTreeView);
|
_playersControl = new BindableListControl<Player>(playerColumns, nameof(Player.PlayerId), PlayersTreeView);
|
||||||
_playersControl.OnSelectionChanged += PlayersControlOnOnSelectionChanged;
|
_playersControl.ItemActivated += PlayersControlActivated;
|
||||||
|
|
||||||
|
|
||||||
var drinkColumns = new List<DataColumn>
|
var drinkColumns = new List<DataColumn>
|
||||||
@@ -53,9 +53,8 @@ namespace EstusShots.Gtk
|
|||||||
|
|
||||||
// Events
|
// 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);
|
var dialog = new PlayerEditor(this, player);
|
||||||
dialog.OnDialogClosed += PlayerEditorClosed;
|
dialog.OnDialogClosed += PlayerEditorClosed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,18 +40,20 @@ namespace EstusShots.Gtk
|
|||||||
await ReloadSeasons();
|
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);
|
using var _ = new LoadingMode(this);
|
||||||
// TODO real season edit control
|
|
||||||
var season = new Season
|
var parameter = new SaveSeasonParameter(args.Model);
|
||||||
{
|
|
||||||
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);
|
var res = await Client.Seasons.SaveSeason(parameter);
|
||||||
if (!res.OperationResult.Success)
|
if (!res.OperationResult.Success)
|
||||||
{
|
{
|
||||||
@@ -61,24 +63,25 @@ namespace EstusShots.Gtk
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ReloadSeasons();
|
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();
|
EpisodesPage.Show();
|
||||||
var parameter = new GetEpisodesParameter(season.SeasonId);
|
var parameter = new GetEpisodesParameter(e.Selection.SeasonId);
|
||||||
var res = await Client.Episodes.GetEpisodes(parameter);
|
var res = await Client.Episodes.GetEpisodes(parameter);
|
||||||
EpisodesControl.Items = res.Data.Episodes;
|
EpisodesControl.Items = res.Data.Episodes;
|
||||||
EpisodesControl.DataBind();
|
EpisodesControl.DataBind();
|
||||||
|
|
||||||
UpdateTitle();
|
UpdateTitle();
|
||||||
Navigation.Page = EpisodesPageNumber;
|
Info($"{e.Selection.DisplayName}: {res.Data.Episodes.Count} episodes");
|
||||||
|
}
|
||||||
|
|
||||||
Info($"{season.DisplayName}: {res.Data.Episodes.Count} episodes");
|
private void SeasonsControlItemActivated(Season item)
|
||||||
|
{
|
||||||
|
var dialog = new SeasonEditor(this, item);
|
||||||
|
dialog.OnDialogClosed += SeasonEditorClosed;
|
||||||
|
dialog.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private Methods
|
// Private Methods
|
||||||
@@ -94,7 +97,8 @@ namespace EstusShots.Gtk
|
|||||||
return;
|
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();
|
SeasonsControl.DataBind();
|
||||||
Info("Seasons Refreshed");
|
Info("Seasons Refreshed");
|
||||||
}
|
}
|
||||||
@@ -103,7 +107,7 @@ namespace EstusShots.Gtk
|
|||||||
{
|
{
|
||||||
var columns = new List<DataColumn>
|
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.Description)),
|
||||||
new DataColumnText(nameof(Season.Start))
|
new DataColumnText(nameof(Season.Start))
|
||||||
{
|
{
|
||||||
@@ -115,7 +119,8 @@ namespace EstusShots.Gtk
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
SeasonsControl = new BindableListControl<Season>(columns, nameof(Season.SeasonId), SeasonsView);
|
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);
|
_context.Players.Update(player);
|
||||||
_mapper.Map(parameter.Player, player);
|
_mapper.Map(parameter.Player, player);
|
||||||
var count = await _context.SaveChangesAsync();
|
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));
|
return new ApiResponse<SavePlayerResponse>(new SavePlayerResponse(player.PlayerId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,18 +46,23 @@ namespace EstusShots.Server.Services
|
|||||||
|
|
||||||
public async Task<ApiResponse<SaveSeasonResponse>> SaveSeason(SaveSeasonParameter parameter)
|
public async Task<ApiResponse<SaveSeasonResponse>> SaveSeason(SaveSeasonParameter parameter)
|
||||||
{
|
{
|
||||||
var season = _mapper.Map<Season>(parameter.Season);
|
if (parameter.Season.SeasonId.IsEmpty())
|
||||||
var existing = await _context.Seasons.FindAsync(season.SeasonId);
|
|
||||||
if (existing == null)
|
|
||||||
{
|
{
|
||||||
_context.Seasons.Add(season);await _context.SaveChangesAsync();
|
var season = _mapper.Map<Season>(parameter.Season);
|
||||||
_logger.LogInformation($"New season created: '{season.SeasonId}'");
|
_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
|
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