diff --git a/CHANGELOG.md b/CHANGELOG.md index 73498bd..799db54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,13 +53,20 @@ This project has many features that were implemented in a [standalone app](https - The tree view doesn't automatically reset when the user does an undo or redo action. You have to close and reopen the treeview for the changes to be reflected. This is annoying, but I can't seem to get my [Main.OnNotification](/JsonToolsNppPlugin/Main.cs) method to respond to undo and redo actions. - Improve how well the caret tracks the node selected in the query tree, after a query that selects a subset of nodes. The iterables have their line number set to 0. -## [3.5.0] (unreleased) - 2022-MM-DD +## [3.5.0] - 2022-09-24 ### Added -1. `Refresh` button for resetting the form with the JSON in the currently active buffer. -2. Clicking on a tree node to expand it also changes the displayed node path and snaps the caret. -3. Drop-down menu option for expanding/collapsing all subtrees when right-clicking on a tree node. +1. `Refresh` button for resetting the form with the JSON in the currently active buffer (resolves Issue #13). +2. __Tree view enhancements__ (resolves Issue #10): + - Clicking on a tree node to expand it also changes the displayed node path and snaps the caret. + - `Tab` can be used to switch between controls on the tree form. + - Drop-down menu option for expanding/collapsing all subtrees when right-clicking on a tree node. + - Query box is auto-selected when tree is opened. + - `Ctrl+Enter` while query box is selected submits the query. + - `Enter` while any button selected is equivalent to clicking it. + - `Enter` while tree is selected toggles the selected node between expanded/collapsed. + - `Escape` key takes focus from the tree back to the text editor (resolves Issue #11). ### Changed diff --git a/JsonToolsNppPlugin/Forms/JsonToCsvForm.Designer.cs b/JsonToolsNppPlugin/Forms/JsonToCsvForm.Designer.cs index 4c66205..bbf59b2 100644 --- a/JsonToolsNppPlugin/Forms/JsonToCsvForm.Designer.cs +++ b/JsonToolsNppPlugin/Forms/JsonToCsvForm.Designer.cs @@ -50,6 +50,7 @@ private void InitializeComponent() this.KeySepBox.Size = new System.Drawing.Size(52, 24); this.KeySepBox.TabIndex = 0; this.KeySepBox.Text = "."; + this.KeySepBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.JsonToCsvForm_KeyUp); // // JsonToCsvFormTitle // @@ -73,6 +74,7 @@ private void InitializeComponent() this.DelimBox.Name = "DelimBox"; this.DelimBox.Size = new System.Drawing.Size(75, 26); this.DelimBox.TabIndex = 1; + this.DelimBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.JsonToCsvForm_KeyUp); // // BoolsToIntsCheckBox // @@ -84,6 +86,7 @@ private void InitializeComponent() this.BoolsToIntsCheckBox.TabIndex = 2; this.BoolsToIntsCheckBox.Text = "Convert true/false to 1/0?"; this.BoolsToIntsCheckBox.UseVisualStyleBackColor = true; + this.BoolsToIntsCheckBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.JsonToCsvForm_KeyUp); // // StrategyBox // @@ -98,6 +101,7 @@ private void InitializeComponent() this.StrategyBox.Name = "StrategyBox"; this.StrategyBox.Size = new System.Drawing.Size(195, 26); this.StrategyBox.TabIndex = 3; + this.StrategyBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.JsonToCsvForm_KeyUp); // // StrategyBoxLabel // @@ -139,6 +143,7 @@ private void InitializeComponent() this.GenerateCSVButton.Text = "Generate CSV"; this.GenerateCSVButton.UseVisualStyleBackColor = true; this.GenerateCSVButton.Click += new System.EventHandler(this.GenerateCSVButton_Click); + this.GenerateCSVButton.KeyUp += new System.Windows.Forms.KeyEventHandler(this.JsonToCsvForm_KeyUp); // // DocsButton // @@ -150,6 +155,7 @@ private void InitializeComponent() this.DocsButton.Text = "Docs"; this.DocsButton.UseVisualStyleBackColor = true; this.DocsButton.Click += new System.EventHandler(this.DocsButton_Click); + this.DocsButton.KeyUp += new System.Windows.Forms.KeyEventHandler(this.JsonToCsvForm_KeyUp); // // JsonToCsvForm // @@ -169,6 +175,7 @@ private void InitializeComponent() this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Name = "JsonToCsvForm"; this.Text = "JSON to CSV"; + this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.JsonToCsvForm_KeyUp); this.ResumeLayout(false); this.PerformLayout(); diff --git a/JsonToolsNppPlugin/Forms/JsonToCsvForm.cs b/JsonToolsNppPlugin/Forms/JsonToCsvForm.cs index 5522cb7..5666070 100644 --- a/JsonToolsNppPlugin/Forms/JsonToCsvForm.cs +++ b/JsonToolsNppPlugin/Forms/JsonToCsvForm.cs @@ -23,6 +23,21 @@ public JsonToCsvForm(JNode json) StrategyBox.SelectedIndex = 0; } + private void JsonToCsvForm_KeyUp(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Escape) + { + e.Handled = true; + Close(); + } + else if (e.KeyCode == Keys.Enter) + { + if (sender is Button btn) + btn.PerformClick(); + e.Handled = true; + } + } + private void GenerateCSVButton_Click(object sender, EventArgs e) { string keysep = new string(KeySepBox.Text[0], 1); diff --git a/JsonToolsNppPlugin/Forms/TreeViewer.Designer.cs b/JsonToolsNppPlugin/Forms/TreeViewer.Designer.cs index 06ef1c2..3b90e6c 100644 --- a/JsonToolsNppPlugin/Forms/TreeViewer.Designer.cs +++ b/JsonToolsNppPlugin/Forms/TreeViewer.Designer.cs @@ -47,9 +47,9 @@ private void InitializeComponent() this.JavaScriptStyleKeyItem = new System.Windows.Forms.ToolStripMenuItem(); this.PythonStylePathItem = new System.Windows.Forms.ToolStripMenuItem(); this.RemesPathStylePathItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ToggleSubtreesItem = new System.Windows.Forms.ToolStripMenuItem(); this.CurrentPathBox = new System.Windows.Forms.TextBox(); this.CurrentPathButton = new System.Windows.Forms.Button(); - this.ToggleSubtreesItem = new System.Windows.Forms.ToolStripMenuItem(); this.RefreshButton = new System.Windows.Forms.Button(); this.NodeRightClickMenu.SuspendLayout(); this.SuspendLayout(); @@ -62,10 +62,11 @@ private void InitializeComponent() this.Tree.Location = new System.Drawing.Point(4, 91); this.Tree.Name = "Tree"; this.Tree.Size = new System.Drawing.Size(398, 339); - this.Tree.TabIndex = 5; + this.Tree.TabIndex = 6; this.Tree.BeforeExpand += new System.Windows.Forms.TreeViewCancelEventHandler(this.Tree_BeforeExpand); this.Tree.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.Tree_AfterSelect); this.Tree.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.Tree_NodeMouseClick); + this.Tree.KeyUp += new System.Windows.Forms.KeyEventHandler(this.TreeViewer_KeyUp); // // TypeIconList // @@ -91,6 +92,8 @@ private void InitializeComponent() this.QueryBox.Size = new System.Drawing.Size(164, 74); this.QueryBox.TabIndex = 0; this.QueryBox.Text = "@"; + this.QueryBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.QueryBox_KeyPress); + this.QueryBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.TreeViewer_KeyUp); // // SubmitQueryButton // @@ -103,6 +106,7 @@ private void InitializeComponent() this.SubmitQueryButton.Text = "Submit query"; this.SubmitQueryButton.UseVisualStyleBackColor = true; this.SubmitQueryButton.Click += new System.EventHandler(this.SubmitQueryButton_Click); + this.SubmitQueryButton.KeyUp += new System.Windows.Forms.KeyEventHandler(this.TreeViewer_KeyUp); // // SaveQueryButton // @@ -114,6 +118,7 @@ private void InitializeComponent() this.SaveQueryButton.Text = "Save query result"; this.SaveQueryButton.UseVisualStyleBackColor = true; this.SaveQueryButton.Click += new System.EventHandler(this.SaveQueryResultButton_Click); + this.SaveQueryButton.KeyUp += new System.Windows.Forms.KeyEventHandler(this.TreeViewer_KeyUp); // // QueryToCsvButton // @@ -125,6 +130,7 @@ private void InitializeComponent() this.QueryToCsvButton.Text = "Query to CSV"; this.QueryToCsvButton.UseVisualStyleBackColor = true; this.QueryToCsvButton.Click += new System.EventHandler(this.QueryToCsvButton_Click); + this.QueryToCsvButton.KeyUp += new System.Windows.Forms.KeyEventHandler(this.TreeViewer_KeyUp); // // FullTreeCheckBox // @@ -133,10 +139,11 @@ private void InitializeComponent() this.FullTreeCheckBox.Location = new System.Drawing.Point(174, 69); this.FullTreeCheckBox.Name = "FullTreeCheckBox"; this.FullTreeCheckBox.Size = new System.Drawing.Size(130, 20); - this.FullTreeCheckBox.TabIndex = 4; + this.FullTreeCheckBox.TabIndex = 5; this.FullTreeCheckBox.Text = "View all subtrees"; this.FullTreeCheckBox.UseVisualStyleBackColor = true; this.FullTreeCheckBox.CheckedChanged += new System.EventHandler(this.FullTreeCheckBox_CheckedChanged); + this.FullTreeCheckBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.TreeViewer_KeyUp); // // NodeRightClickMenu // @@ -211,6 +218,12 @@ private void InitializeComponent() this.RemesPathStylePathItem.Size = new System.Drawing.Size(198, 26); this.RemesPathStylePathItem.Text = "RemesPath style"; // + // ToggleSubtreesItem + // + this.ToggleSubtreesItem.Name = "ToggleSubtreesItem"; + this.ToggleSubtreesItem.Size = new System.Drawing.Size(267, 24); + this.ToggleSubtreesItem.Text = "Expand/collapse all subtrees"; + // // CurrentPathBox // this.CurrentPathBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) @@ -219,7 +232,7 @@ private void InitializeComponent() this.CurrentPathBox.Name = "CurrentPathBox"; this.CurrentPathBox.ReadOnly = true; this.CurrentPathBox.Size = new System.Drawing.Size(299, 22); - this.CurrentPathBox.TabIndex = 7; + this.CurrentPathBox.TabIndex = 8; this.CurrentPathBox.TabStop = false; // // CurrentPathButton @@ -228,16 +241,11 @@ private void InitializeComponent() this.CurrentPathButton.Location = new System.Drawing.Point(4, 435); this.CurrentPathButton.Name = "CurrentPathButton"; this.CurrentPathButton.Size = new System.Drawing.Size(93, 23); - this.CurrentPathButton.TabIndex = 6; + this.CurrentPathButton.TabIndex = 7; this.CurrentPathButton.Text = "Current path"; this.CurrentPathButton.UseVisualStyleBackColor = true; this.CurrentPathButton.Click += new System.EventHandler(this.CurrentPathButton_Click); - // - // ToggleSubtreesItem - // - this.ToggleSubtreesItem.Name = "ToggleSubtreesItem"; - this.ToggleSubtreesItem.Size = new System.Drawing.Size(267, 24); - this.ToggleSubtreesItem.Text = "Expand/collapse all subtrees"; + this.CurrentPathButton.KeyUp += new System.Windows.Forms.KeyEventHandler(this.TreeViewer_KeyUp); // // RefreshButton // @@ -245,10 +253,11 @@ private void InitializeComponent() this.RefreshButton.Location = new System.Drawing.Point(299, 36); this.RefreshButton.Name = "RefreshButton"; this.RefreshButton.Size = new System.Drawing.Size(103, 27); - this.RefreshButton.TabIndex = 8; + this.RefreshButton.TabIndex = 4; this.RefreshButton.Text = "Refresh"; this.RefreshButton.UseVisualStyleBackColor = true; this.RefreshButton.Click += new System.EventHandler(this.RefreshButton_Click); + this.RefreshButton.KeyUp += new System.Windows.Forms.KeyEventHandler(this.TreeViewer_KeyUp); // // TreeViewer // @@ -268,6 +277,7 @@ private void InitializeComponent() this.Name = "TreeViewer"; this.Text = "TreeViewer"; this.VisibleChanged += new System.EventHandler(this.TreeViewer_OnVisibleChanged); + this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.TreeViewer_KeyUp); this.NodeRightClickMenu.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -278,7 +288,6 @@ private void InitializeComponent() private System.Windows.Forms.TreeView Tree; private System.Windows.Forms.ImageList TypeIconList; - private System.Windows.Forms.TextBox QueryBox; private System.Windows.Forms.Button SubmitQueryButton; private System.Windows.Forms.Button SaveQueryButton; private System.Windows.Forms.Button QueryToCsvButton; @@ -297,5 +306,6 @@ private void InitializeComponent() private System.Windows.Forms.Button CurrentPathButton; private System.Windows.Forms.ToolStripMenuItem ToggleSubtreesItem; private System.Windows.Forms.Button RefreshButton; + internal System.Windows.Forms.TextBox QueryBox; } } \ No newline at end of file diff --git a/JsonToolsNppPlugin/Forms/TreeViewer.cs b/JsonToolsNppPlugin/Forms/TreeViewer.cs index 80b8f41..b329feb 100644 --- a/JsonToolsNppPlugin/Forms/TreeViewer.cs +++ b/JsonToolsNppPlugin/Forms/TreeViewer.cs @@ -74,10 +74,55 @@ public TreeViewer(JNode json) //this.Tree.BeforeExpand += new TreeViewCancelEventHandler( // PopulateIfUnpopulatedHandler //); - //QueryBox.Select(); // activate the query box so user can start typing immediately } + // largely copied from NppManagedPluginDemo.cs in the original plugin pack + private void TreeViewer_KeyUp(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter) + { + e.Handled = true; + // Ctrl+Enter in query box -> submit query + if (e.Control && QueryBox.Focused) + SubmitQueryButton.PerformClick(); + else if (sender is Button btn) + { + // Enter has the same effect as clicking a selected button + btn.PerformClick(); + } + else if (sender is TreeView) + { + // Enter in the TreeView toggles the selected node + TreeNode selected = Tree.SelectedNode; + if (selected == null || selected.Nodes.Count == 0) + return; + if (selected.IsExpanded) + selected.Collapse(true); // don't collapse the children as well + else selected.Expand(); + } + } + // Escape -> go to editor + else if (e.KeyData == Keys.Escape) + { + Npp.editor.GrabFocus(); + } + // Tab -> go through controls, Shift+Tab -> go through controls backward + else if (e.KeyCode == Keys.Tab) + { + Control next = GetNextControl((Control)sender, !e.Shift); + while ((next == null) || (!next.TabStop)) next = GetNextControl(next, !e.Shift); + next.Focus(); + e.Handled = true; + } + } + + private void QueryBox_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == '\t') + e.Handled = true; + } + public static void SetImageOfTreeNode(TreeNode root, JNode json) { switch (json.type) diff --git a/JsonToolsNppPlugin/Forms/TreeViewer.resx b/JsonToolsNppPlugin/Forms/TreeViewer.resx index 16016ce..7492d8c 100644 --- a/JsonToolsNppPlugin/Forms/TreeViewer.resx +++ b/JsonToolsNppPlugin/Forms/TreeViewer.resx @@ -125,7 +125,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADC - CgAAAk1TRnQBSQFMAgEBCAEAAagBAAGoAQABEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + CgAAAk1TRnQBSQFMAgEBCAEAAegBAAHoAQABEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo AwABQAMAATADAAEBAQABCAYAAQwYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA diff --git a/JsonToolsNppPlugin/Main.cs b/JsonToolsNppPlugin/Main.cs index e42c68e..15b6160 100644 --- a/JsonToolsNppPlugin/Main.cs +++ b/JsonToolsNppPlugin/Main.cs @@ -403,7 +403,6 @@ public static void ShowTreeView(TreeViewer tree) /// static void OpenJsonTree(bool is_json_lines = false) { - string cur_fname = Npp.notepad.GetCurrentFilePath(); //TreeViewer tv = null; //if (IsWin32) // treeViewers.TryGetValue(cur_fname, out tv); @@ -414,6 +413,7 @@ static void OpenJsonTree(bool is_json_lines = false) // if the tree view is open, hide the tree view and then dispose of it HideTreeView(treeViewer); treeViewer.Close(); + treeViewer = null; //if (IsWin32) // treeViewers.Remove(cur_fname); //else treeViewer = null; @@ -459,7 +459,10 @@ static void OpenJsonTree(bool is_json_lines = false) treeViewer.JsonTreePopulate(json); //if (IsWin32) // treeViewers[cur_fname] = tv; - treeViewer.Focus(); + treeViewer.QueryBox.Focus(); + // select QueryBox on startup + // note that this is only possible because we changed the access modifier + // of that control from private (the default) to internal. } #endregion } diff --git a/JsonToolsNppPlugin/Release_x64.zip b/JsonToolsNppPlugin/Release_x64.zip index 2c9a9f8..749f51d 100644 Binary files a/JsonToolsNppPlugin/Release_x64.zip and b/JsonToolsNppPlugin/Release_x64.zip differ diff --git a/JsonToolsNppPlugin/Release_x86.zip b/JsonToolsNppPlugin/Release_x86.zip index e85b3c2..56c9b45 100644 Binary files a/JsonToolsNppPlugin/Release_x86.zip and b/JsonToolsNppPlugin/Release_x86.zip differ diff --git a/docs/README.md b/docs/README.md index c43e0c9..8eb1700 100644 --- a/docs/README.md +++ b/docs/README.md @@ -59,6 +59,11 @@ You can click on the nodes in that tree to see the children. When you select a n __NOTES__ 1. If you submit a RemesPath query that is anything other than the default `@`, the JSON tree may no longer send the caret to the correct line. 2. If you [edit your JSON](/docs/RemesPath.md#editing-with-assignment-expressions) with RemesPath queries and then undo your change with `Ctrl+Z` or similar, that will not undo the changes to the JSON. To re-sync the JSON with the document, you will have to close and then re-open the tree view. +3. Keyboard shortcuts: + - `Ctrl+Enter` in the query box submits the query. + - `Enter` while the tree is selected toggles the selected node between expanded/collapsed. + - Up and down arrow keys can also navigate the tree. + - `Escape` takes focus from the tree view back to the editor. If a node has a `+` or `-` sign next to it, you can click on that button to expand the children of the node, as shown here.