Add lookup control
This commit is contained in:
@@ -21,6 +21,8 @@ namespace EstusShots.Gtk.Controls
|
|||||||
|
|
||||||
public delegate void ItemActivatedEventHandler<T>(T item);
|
public delegate void ItemActivatedEventHandler<T>(T item);
|
||||||
|
|
||||||
|
public delegate void RowContextMenuHandler<T>(T selection);
|
||||||
|
|
||||||
public class BindableListControl<T>
|
public class BindableListControl<T>
|
||||||
{
|
{
|
||||||
/// <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>
|
||||||
@@ -51,6 +53,11 @@ namespace EstusShots.Gtk.Controls
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event ItemActivatedEventHandler<T> ItemActivated;
|
public event ItemActivatedEventHandler<T> ItemActivated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will be invoked when a row is right clicked
|
||||||
|
/// </summary>
|
||||||
|
public event RowContextMenuHandler<T> RowContextMenuOpened;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize a new BindableListView with an existing TreeView Widget
|
/// Initialize a new BindableListView with an existing TreeView Widget
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -68,6 +75,7 @@ namespace EstusShots.Gtk.Controls
|
|||||||
Items = new List<T>();
|
Items = new List<T>();
|
||||||
|
|
||||||
TreeView.RowActivated += TreeViewOnRowActivated;
|
TreeView.RowActivated += TreeViewOnRowActivated;
|
||||||
|
TreeView.ButtonReleaseEvent += TreeViewOnButtonReleaseEvent;
|
||||||
TreeView.Selection.Changed += TreeViewSelectionOnChanged;
|
TreeView.Selection.Changed += TreeViewSelectionOnChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +161,12 @@ namespace EstusShots.Gtk.Controls
|
|||||||
SelectionChanged?.Invoke(selection, new SelectionChangedEventArgs<T>(SelectedItem));
|
SelectionChanged?.Invoke(selection, new SelectionChangedEventArgs<T>(SelectedItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TreeViewOnButtonReleaseEvent(object o, ButtonReleaseEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.Event.Button != 3) return;
|
||||||
|
RowContextMenuOpened?.Invoke(SelectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
private void InitTreeViewColumns()
|
private void InitTreeViewColumns()
|
||||||
{
|
{
|
||||||
foreach (var dataColumn in Columns)
|
foreach (var dataColumn in Columns)
|
||||||
@@ -175,7 +189,6 @@ namespace EstusShots.Gtk.Controls
|
|||||||
if (gType.ToString() == "GtkSharpValue") gType = MapType(propType);
|
if (gType.ToString() == "GtkSharpValue") gType = MapType(propType);
|
||||||
return gType;
|
return gType;
|
||||||
});
|
});
|
||||||
var data = new DateTime();
|
|
||||||
// The first column in the data store is always the key field.
|
// The first column in the data store is always the key field.
|
||||||
var columns = new List<GType> {(GType) typeof(T).GetProperty(KeyField)?.PropertyType};
|
var columns = new List<GType> {(GType) typeof(T).GetProperty(KeyField)?.PropertyType};
|
||||||
columns.AddRange(types);
|
columns.AddRange(types);
|
||||||
|
|||||||
78
EstusShots.Gtk/Controls/Controls.glade
Normal file
78
EstusShots.Gtk/Controls/Controls.glade
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.22.1 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<object class="GtkBox" id="_multiLookupControl">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">5</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">5</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkComboBox" id="_searchBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="has_entry">True</property>
|
||||||
|
<child internal-child="entry">
|
||||||
|
<object class="GtkEntry">
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="primary_icon_name">edit-find-symbolic</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="_addButton">
|
||||||
|
<property name="label" translatable="yes">Add</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</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="vexpand">True</property>
|
||||||
|
<property name="shadow_type">in</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeView" id="_selectionTreeView">
|
||||||
|
<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="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
114
EstusShots.Gtk/Controls/LookupSelectionControl.cs
Normal file
114
EstusShots.Gtk/Controls/LookupSelectionControl.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Gdk;
|
||||||
|
using GLib;
|
||||||
|
using Gtk;
|
||||||
|
using Key = Gtk.Key;
|
||||||
|
using Menu = Gtk.Menu;
|
||||||
|
using MenuItem = Gtk.MenuItem;
|
||||||
|
using UI = Gtk.Builder.ObjectAttribute;
|
||||||
|
|
||||||
|
namespace EstusShots.Gtk.Controls
|
||||||
|
{
|
||||||
|
public class LookupSelectionControlOptions<T>
|
||||||
|
{
|
||||||
|
public List<DataColumn> Columns { get; set; }
|
||||||
|
public List<T> SearchSpace { get; set; }
|
||||||
|
public string KeyProperty { get; set; }
|
||||||
|
public string DisplayProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LookupSelectionControl<T> : Box
|
||||||
|
{
|
||||||
|
[UI] private readonly ComboBox _searchBox = null;
|
||||||
|
[UI] private readonly TreeView _selectionTreeView = null;
|
||||||
|
[UI] private readonly Button _addButton = null;
|
||||||
|
private readonly BindableListControl<T> _selectedItemsListControl;
|
||||||
|
|
||||||
|
public string KeyProperty { get; }
|
||||||
|
public string DisplayProperty { get; }
|
||||||
|
public List<T> AllItems { get; }
|
||||||
|
public List<T> SelectedItems { get; set; }
|
||||||
|
|
||||||
|
public LookupSelectionControl(LookupSelectionControlOptions<T> options) :
|
||||||
|
this(options, new Builder("Controls.glade"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public LookupSelectionControl(LookupSelectionControlOptions<T> options, Builder builder) :
|
||||||
|
base(builder.GetObject("_multiLookupControl").Handle)
|
||||||
|
{
|
||||||
|
builder.Autoconnect(this);
|
||||||
|
|
||||||
|
KeyProperty = options.KeyProperty;
|
||||||
|
DisplayProperty = options.DisplayProperty;
|
||||||
|
AllItems = options.SearchSpace;
|
||||||
|
SelectedItems = new List<T>();
|
||||||
|
|
||||||
|
var completionStore = new ListStore(GType.String, GType.String);
|
||||||
|
BindCompletionList(completionStore);
|
||||||
|
|
||||||
|
_searchBox.Model = completionStore;
|
||||||
|
_searchBox.IdColumn = 0;
|
||||||
|
_searchBox.EntryTextColumn = 1;
|
||||||
|
|
||||||
|
_searchBox.Entry.Completion = new EntryCompletion
|
||||||
|
{
|
||||||
|
Model = completionStore,
|
||||||
|
InlineSelection = true,
|
||||||
|
InlineCompletion = true,
|
||||||
|
TextColumn = 1,
|
||||||
|
PopupCompletion = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Init the selected items TreeView
|
||||||
|
_selectedItemsListControl = new BindableListControl<T>(options.Columns, KeyProperty, _selectionTreeView)
|
||||||
|
{
|
||||||
|
Items = SelectedItems
|
||||||
|
};
|
||||||
|
_selectionTreeView.CanFocus = false;
|
||||||
|
|
||||||
|
_selectedItemsListControl.RowContextMenuOpened += selection =>
|
||||||
|
{
|
||||||
|
var remove = new MenuItem {Label = "Remove"};
|
||||||
|
remove.Activated += (sender, eventArgs) =>
|
||||||
|
{
|
||||||
|
SelectedItems.Remove(_selectedItemsListControl.SelectedItem);
|
||||||
|
_selectedItemsListControl.DataBind();
|
||||||
|
};
|
||||||
|
var menu = new Menu {remove};
|
||||||
|
menu.ShowAll();
|
||||||
|
menu.Popup();
|
||||||
|
};
|
||||||
|
|
||||||
|
_addButton.Clicked += (sender, args) => AddSelectionToTreeView();
|
||||||
|
_searchBox.Entry.Activated += (sender, args) => AddSelectionToTreeView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddSelectionToTreeView()
|
||||||
|
{
|
||||||
|
// FInd item preferably via its ID, try matching the display value as fallback
|
||||||
|
var item = Guid.TryParse(_searchBox.ActiveId, out var selectedSeason)
|
||||||
|
? AllItems.FirstOrDefault(x => x.GetPropertyValue(KeyProperty).Equals(selectedSeason))
|
||||||
|
: AllItems.FirstOrDefault(y => y.GetPropertyValue(DisplayProperty).Equals(_searchBox.Entry.Text));
|
||||||
|
if (item == null) return;
|
||||||
|
|
||||||
|
if (!SelectedItems.Contains(item)) SelectedItems.Add(item);
|
||||||
|
_selectedItemsListControl.DataBind();
|
||||||
|
_searchBox.Entry.Text = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BindCompletionList(ListStore store)
|
||||||
|
{
|
||||||
|
foreach (var item in AllItems)
|
||||||
|
{
|
||||||
|
// value of key
|
||||||
|
var keyValue = item.GetType().GetProperty(KeyProperty)?.GetValue(item).ToString();
|
||||||
|
// value for display
|
||||||
|
var displayValue = item.GetType().GetProperty(DisplayProperty)?.GetValue(item);
|
||||||
|
store.AppendValues(keyValue, displayValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,39 +10,33 @@ namespace EstusShots.Gtk.Dialogs
|
|||||||
{
|
{
|
||||||
[UI] private readonly Entry _nameEntry = null;
|
[UI] private readonly Entry _nameEntry = null;
|
||||||
[UI] private readonly CheckButton _isBossCheckButton = null;
|
[UI] private readonly CheckButton _isBossCheckButton = null;
|
||||||
[UI] private readonly SearchEntry _searchSeasonEntry = null;
|
[UI] private readonly Box _seasonSelectionContainer = null;
|
||||||
[UI] private readonly TreeView _selectedSeasonsTreeView = null;
|
|
||||||
|
|
||||||
private BindableListControl<Season> _selectedSeasonsControl;
|
private LookupSelectionControl<Season> _seasonSelectionControl;
|
||||||
private readonly List<Season> _allSeasons;
|
|
||||||
private readonly EntryCompletion _allSeasonsCompletion;
|
|
||||||
|
|
||||||
public EnemyEditor(Window parent, Enemy enemy, List<Season> seasons) :
|
public EnemyEditor(Window parent, Enemy enemy, List<Season> seasons) :
|
||||||
base(parent, new Builder("EnemyEditor.glade"))
|
base(parent, new Builder("EnemyEditor.glade"))
|
||||||
{
|
{
|
||||||
EditObject = enemy;
|
EditObject = enemy;
|
||||||
_allSeasons = seasons;
|
_seasonSelectionControl = new LookupSelectionControl<Season>(new LookupSelectionControlOptions<Season>
|
||||||
|
|
||||||
var columns = new List<DataColumn>
|
|
||||||
{
|
{
|
||||||
new DataColumnText(nameof(Season.DisplayName)) {Title = "Seasons"}
|
KeyProperty = nameof(Season.SeasonId),
|
||||||
};
|
DisplayProperty = nameof(Season.DisplayName),
|
||||||
_selectedSeasonsControl =
|
Columns = new List<DataColumn>
|
||||||
new BindableListControl<Season>(columns, nameof(Season.SeasonId), _selectedSeasonsTreeView);
|
{
|
||||||
|
new DataColumnText(nameof(Season.DisplayName)) {Title = "Seasons"}
|
||||||
_allSeasonsCompletion = new EntryCompletion();
|
},
|
||||||
|
SearchSpace = seasons
|
||||||
_searchSeasonEntry.Completion = new EntryCompletion();
|
});
|
||||||
|
_seasonSelectionContainer.PackStart(_seasonSelectionControl, true, true, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadToModel()
|
protected override void LoadToModel()
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadFromModel()
|
protected override void LoadFromModel()
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
<!-- Generated with glade 3.22.1 -->
|
<!-- Generated with glade 3.22.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.20"/>
|
<requires lib="gtk+" version="3.20"/>
|
||||||
<object class="GtkEntryCompletion" id="entrycompletion1"/>
|
|
||||||
<object class="GtkDialog" id="_editorDialog">
|
<object class="GtkDialog" id="_editorDialog">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="title" translatable="yes">Enemy</property>
|
<property name="title" translatable="yes">Enemy</property>
|
||||||
@@ -10,7 +9,7 @@
|
|||||||
<property name="modal">True</property>
|
<property name="modal">True</property>
|
||||||
<property name="window_position">center-on-parent</property>
|
<property name="window_position">center-on-parent</property>
|
||||||
<property name="default_width">350</property>
|
<property name="default_width">350</property>
|
||||||
<property name="default_height">250</property>
|
<property name="default_height">300</property>
|
||||||
<property name="destroy_with_parent">True</property>
|
<property name="destroy_with_parent">True</property>
|
||||||
<property name="type_hint">dialog</property>
|
<property name="type_hint">dialog</property>
|
||||||
<property name="gravity">center</property>
|
<property name="gravity">center</property>
|
||||||
@@ -119,45 +118,19 @@
|
|||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSearchEntry" id="_searchSeasonEntry">
|
<object class="GtkBox" id="_seasonSelectionContainer">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="primary_icon_name">edit-find-symbolic</property>
|
<property name="orientation">vertical</property>
|
||||||
<property name="primary_icon_activatable">False</property>
|
<child>
|
||||||
<property name="primary_icon_sensitive">False</property>
|
<placeholder/>
|
||||||
<property name="completion">entrycompletion1</property>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left_attach">1</property>
|
<property name="left_attach">1</property>
|
||||||
<property name="top_attach">2</property>
|
<property name="top_attach">2</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
<property name="vexpand">True</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkTreeView" id="_selectedSeasonsTreeView">
|
|
||||||
<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="left_attach">1</property>
|
|
||||||
<property name="top_attach">3</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<placeholder/>
|
<placeholder/>
|
||||||
</child>
|
</child>
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ namespace EstusShots.Gtk
|
|||||||
|
|
||||||
private void NewEnemyButtonOnClicked(object sender, EventArgs e)
|
private void NewEnemyButtonOnClicked(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
|
var enemyEditor = new EnemyEditor(this, new Enemy(), SeasonsControl.Items);
|
||||||
|
enemyEditor.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ReloadEnemies()
|
private async void ReloadEnemies()
|
||||||
|
|||||||
Reference in New Issue
Block a user