Skip to content

Commit

Permalink
more key bindings, attributes editing enhanced
Browse files Browse the repository at this point in the history
  • Loading branch information
my-th-os committed May 7, 2019
1 parent bbc2ea7 commit 6debcaa
Show file tree
Hide file tree
Showing 12 changed files with 541 additions and 108 deletions.
15 changes: 14 additions & 1 deletion CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
Version 0.8.1 - in progress
- more key bindings for insert, delete, copy & paste
- copy & paste attributes
- more keyboard controls
- insert [Ins], delete [Del], copy [Ctrl-C] & paste [Ctrl+V]
- switch view on vessels/kerbals [Ctrl+Enter]
- from tree to attributes [Enter]
- from attribute to edit value [Enter]
- navigate attribute remaining in edit mode [Up]/[Down]
- from edit value back to attributes [Enter]
- from edit value back to attributes with undo [Esc]
- from attributes back to tree [Esc]/[Left]
- tooltip mentions that you were always able switch tabs [Ctrl+Tab]
- attributes in kerbals tab now with same functionality as in tree
- bugfix about paste command checking for clipboard content
- bugfix about duplicate attributes like name, type (testing copy & paste)
- no recent KSP versions tested (pretty shure it'll work fine, please report)

Version 0.8 - 2018-05-05
- copy & paste nodes in the tree - thanks alot to pamidur's contribution
Expand Down
Binary file modified KML.zip
Binary file not shown.
15 changes: 14 additions & 1 deletion KML/CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
Version 0.8.1 - in progress
- more key bindings for insert, delete, copy & paste
- copy & paste attributes
- more keyboard controls
- insert [Ins], delete [Del], copy [Ctrl-C] & paste [Ctrl+V]
- switch view on vessels/kerbals [Ctrl+Enter]
- from tree to attributes [Enter]
- from attribute to edit value [Enter]
- navigate attribute remaining in edit mode [Up]/[Down]
- from edit value back to attributes [Enter]
- from edit value back to attributes with undo [Esc]
- from attributes back to tree [Esc]/[Left]
- tooltip mentions that you were always able switch tabs [Ctrl+Tab]
- attributes in kerbals tab now with same functionality as in tree
- bugfix about paste command checking for clipboard content
- bugfix about duplicate attributes like name, type (testing copy & paste)
- no recent KSP versions tested (pretty shure it'll work fine, please report)

Version 0.8 - 2018-05-05
- copy & paste nodes in the tree - thanks alot to pamidur's contribution
Expand Down
104 changes: 99 additions & 5 deletions KML/GUI/GuiKebalsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace KML
{
Expand All @@ -27,6 +28,10 @@ class GuiKebalsManager : IGuiManager
private Label KerbalsCount { get; set; }
private KmlNode Roster { get; set; }

private KmlKerbal _oldSelectedKerbal;
private KmlItem _oldSelectedAttrib;
private KmlItem _alternativeSelectedAttrib;

/// <summary>
/// Creates a GuiKebalsManager to link and manage the given two ListViews.
/// </summary>
Expand Down Expand Up @@ -151,13 +156,46 @@ public void Previous()
/// </summary>
public void CommandExec(string Command)
{
if (KerbalsDetails.IsKeyboardFocusWithin)
if (KerbalsDetails.IsKeyboardFocusWithin && KerbalsDetails.SelectedItem is GuiTreeAttrib)
{
// TODO GuiKerbalsManager.CommandExec() for KerbalsDetails
if (Command == "Enter")
{
// Enter the TextBox for value editing
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
(KerbalsDetails.SelectedItem as ListViewItem).MoveFocus(request);
}
else if (Command == "Escape" || Command == "Left")
{
// Get back to TreeView
KerbalsList.Focus();
(KerbalsList.SelectedItem as ListViewItem).Focus();
}
else
{
(KerbalsDetails.SelectedItem as GuiTreeAttrib).CommandExec(Command);
}
}
else if (KerbalsList.IsKeyboardFocusWithin && KerbalsList.SelectedItem is GuiKerbalsNode)
{
(KerbalsList.SelectedItem as GuiKerbalsNode).CommandExec(Command);
if (Command == "Enter" || Command == "Right")
{
// Switch to attributes
KerbalsDetails.Focus();
KmlItem item = GetSelectedItem();
if (item is KmlNode)
{
KmlNode node = (KmlNode)item;
if (node.Attribs.Count > 0)
{
Select(node.Attribs[0]);
(KerbalsDetails.SelectedItem as ListViewItem).Focus();
}
}
}
else
{
(KerbalsList.SelectedItem as GuiKerbalsNode).CommandExec(Command);
}
}
}

Expand All @@ -169,15 +207,27 @@ public void CommandExec(string Command)
/// <returns>Whether item was found or not</returns>
public bool Select(KmlItem item)
{
KmlNode masterNode = item is KmlNode ? (KmlNode)item : item.Parent;
foreach (GuiKerbalsNode node in KerbalsList.Items)
{
if (node.DataKerbal == item)
if (node.DataKerbal == masterNode)
{
// Force a refreh, by causing SelectionChanged to invoke
KerbalsList.SelectedItem = null;
KerbalsList.SelectedItem = node;
KerbalsList.ScrollIntoView(node);
Focus();
if (item is KmlAttrib)
{
foreach (GuiTreeAttrib attrib in KerbalsDetails.Items)
{
if (attrib.DataAttrib == item)
{
attrib.IsSelected = true;
KerbalsDetails.Focus();
}
}
}
return true;
}
}
Expand Down Expand Up @@ -326,14 +376,58 @@ private void KerbalsChanged(object sender, RoutedEventArgs e)

private void KerbalsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Store currently selected attribute, in case we just unselect + select the current item to refresh
if (KerbalsList.SelectedItem == null)
{
// We're unselcting, this is the old data we want to restore in future call of this event
GuiTreeAttrib attrib = (KerbalsDetails.SelectedItem as GuiTreeAttrib);
if (attrib == null)
{
_oldSelectedAttrib = null;
_alternativeSelectedAttrib = null;
}
else
{
_oldSelectedAttrib = attrib.DataAttrib;
int i = KerbalsDetails.SelectedIndex;
if (i < KerbalsDetails.Items.Count - 1)
{
_alternativeSelectedAttrib = (KerbalsDetails.Items[i + 1] as GuiTreeAttrib).DataAttrib;
}
else if (i > 0)
{
_alternativeSelectedAttrib = (KerbalsDetails.Items[i - 1] as GuiTreeAttrib).DataAttrib;
}
else
{
_alternativeSelectedAttrib = null;
}
}
}

KerbalsDetails.Items.Clear();
if (KerbalsList.SelectedItem != null)
{
GuiKerbalsNode Node = (GuiKerbalsNode)KerbalsList.SelectedItem;
KerbalsDetails.ContextMenu = Node.ContextMenu;
foreach (KmlAttrib attrib in Node.DataKerbal.Attribs)
{
KerbalsDetails.Items.Add(attrib);
KerbalsDetails.Items.Add(new GuiTreeAttrib(attrib));
}
_oldSelectedKerbal = Node.DataKerbal;

// Restore attrib selection
if (Node.DataKerbal == _oldSelectedKerbal && _oldSelectedAttrib != null)
{
if (!Select(_oldSelectedAttrib))
Select(_alternativeSelectedAttrib);
if (KerbalsDetails.SelectedItem != null)
(KerbalsDetails.SelectedItem as ListViewItem).Focus();
}
}
else
{
KerbalsDetails.ContextMenu = null;
}
}

Expand Down
12 changes: 12 additions & 0 deletions KML/GUI/GuiKerbalsNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public GuiKerbalsNode(KmlKerbal dataKerbal)

// Get notified when KmlNode ToString() changes
DataKerbal.ToStringChanged += DataKerbal_ToStringChanged;
// Get notified when attributes are added / deleted
DataKerbal.AttribChanged += DataKerbal_AttribChanged;
}

/// <summary>
Expand Down Expand Up @@ -183,6 +185,16 @@ private TextBlock GenerateStateText(KmlKerbal kerbal, TextBlock previous)
return text;
}

private void DataKerbal_AttribChanged(object sender, RoutedEventArgs e)
{
// Refresh details view if selected
if (IsSelected)
{
IsSelected = false;
IsSelected = true;
}
}

private void DataKerbal_ToStringChanged(object sender, System.Windows.RoutedEventArgs e)
{
AssignTemplate();
Expand Down
113 changes: 113 additions & 0 deletions KML/GUI/GuiTreeAttrib.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
Expand Down Expand Up @@ -34,6 +35,11 @@ private set

private static GuiIcons Icons = new GuiIcons16();

private MenuItem MenuInsert { get; set; }
private MenuItem MenuDelete { get; set; }
private MenuItem MenuCopy { get; set; }
private MenuItem MenuPaste { get; set; }

/// <summary>
/// Creates a GuiTreeNode containing the given dataAttrib.
/// </summary>
Expand All @@ -45,6 +51,33 @@ public GuiTreeAttrib(KmlAttrib dataAttrib)
BuildContextMenu();
}

/// <summary>
/// Some key was pressed.
/// </summary>
public void CommandExec(string Command)
{
ContextMenuUpdate(ContextMenu);
switch (Command)
{
case "Insert":
if (MenuInsert != null && MenuInsert.IsEnabled)
AttribInsertBefore_Click(MenuInsert, null);
break;
case "Delete":
if (MenuDelete != null && MenuDelete.IsEnabled)
AttribDelete_Click(MenuDelete, null);
break;
case "Copy":
if (MenuCopy != null && MenuCopy.IsEnabled)
AttribCopy_Click(MenuCopy, null);
break;
case "Paste":
if (MenuPaste != null && MenuPaste.IsEnabled)
AttribPasteBefore_Click(MenuPaste, null);
break;
}
}

private void BuildContextMenu()
{
string shortHeader = DataAttrib.ToString();
Expand All @@ -62,30 +95,59 @@ private void BuildContextMenu()
title.BorderBrush = new SolidColorBrush(Colors.Gray);
menu.Items.Add(title);
menu.Items.Add(new Separator());
menu.Opened += ContextMenu_Opened;

// So far it's the default Menu, wich should not be shown if no items follow
int defaultMenuCount = menu.Items.Count;

MenuItem m = new MenuItem();
m.DataContext = DataAttrib;
m.Icon = Icons.CreateImage(Icons.Clipboard);
m.Header = "_Copy attribute";
m.InputGestureText = "[Ctrl+C]";
m.Click += AttribCopy_Click;
MenuCopy = m;
menu.Items.Add(m);

m = new MenuItem();
m.DataContext = DataAttrib;
m.Icon = Icons.CreateImage(Icons.Paste);
m.Header = "_Paste attribute(s)";
m.InputGestureText = "[Ctrl+V]";
m.Click += AttribPasteBefore_Click;
// m.IsEnabled = Clipboard.ContainsText(TextDataFormat.UnicodeText);
MenuPaste = m;
m.Tag = "Clipboard.Paste";
menu.Items.Add(m);

menu.Items.Add(new Separator());

m = new MenuItem();
m.DataContext = DataAttrib;
m.Icon = Icons.CreateImage(Icons.Add);
m.Header = "_Insert attribute...";
m.InputGestureText = "[Ins]";
m.Click += AttribInsertBefore_Click;
MenuInsert = m;
menu.Items.Add(m);

menu.Items.Add(new Separator());

m = new MenuItem();
m.DataContext = DataAttrib;
m.Icon = Icons.CreateImage(Icons.Delete);
m.Header = "_Delete this attribute...";
m.InputGestureText = "[Del]";
m.Click += AttribDelete_Click;
m.IsEnabled = DataAttrib.CanBeDeleted;
if (!m.IsEnabled && m.Icon != null)
{
(m.Icon as Image).Opacity = 0.3;
}
MenuDelete = m;
menu.Items.Add(m);


// Need to have a seperate menu for each item, even if it is empty.
// If ContextMenu is null, the parent's contextmenu will be used (WTF).
// Item[0] is the menu title, Item[1] a Seperator, both always created.
Expand All @@ -96,6 +158,57 @@ private void BuildContextMenu()
}
}

private void ContextMenuUpdate(ContextMenu menu)
{
foreach (object o in menu.Items)
if (o is MenuItem)
{
MenuItem m = (MenuItem)o;
if (m.Tag != null && (m.Tag as string).Equals("Clipboard.Paste"))
m.IsEnabled = Clipboard.ContainsText(TextDataFormat.UnicodeText);
}
}

private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
ContextMenuUpdate(sender as ContextMenu);
}

private void AttribCopy_Click(object sender, RoutedEventArgs e)
{
KmlAttrib attrib = ((sender as MenuItem).DataContext as KmlAttrib);

var sr = new StringWriter();

KmlItem.WriteItem(sr, attrib, 0);

sr.Flush();

var textNode = sr.GetStringBuilder().ToString();

Clipboard.SetDataObject(textNode);
}

private void AttribPasteBefore_Click(object sender, RoutedEventArgs e)
{
KmlAttrib attrib = ((sender as MenuItem).DataContext as KmlAttrib);
if (attrib.Parent != null)
{
var textNode = Clipboard.GetText(TextDataFormat.UnicodeText);

var items = KmlItem.ParseItems(new StringReader(textNode)).Where(i => i is KmlAttrib).ToList();

if (!items.Any())
DlgMessage.Show("Can not paste attribute from clipboard", "Paste attribute", Icons.Warning);

attrib.Parent.InsertBeforeRange(attrib, items);
}
else
{
DlgMessage.Show("Can not insert, attribute has no parent", "Paste attribute", Icons.Warning);
}
}

private void AttribInsertBefore_Click(object sender, RoutedEventArgs e)
{
// TODO GuiTreeAttrib.AttribInsertBefore_Click(): Almost same code as private GuiTreeNode.AddAttrib()
Expand Down
Loading

0 comments on commit 6debcaa

Please sign in to comment.