From 836626bdc33d0dd12f214dc0fb475e1fb0f332de Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Mon, 25 Nov 2019 14:27:38 -0800 Subject: [PATCH] Work in progress on visualization Basic infrastructure for taking a list of parameters from a plugin and turning it into a collection of UI controls, merging in values from a Visualization object. Doesn't yet do anything useful. WPF makes the hard things easy and the easy things hard. This was a hard thing, so it was easy to do (with some helpful sample code). Yay WPF? --- PluginCommon/Interfaces.cs | 49 +++++ SourceGen/DisasmProject.cs | 5 + SourceGen/LineListGen.cs | 2 +- SourceGen/MainController.cs | 4 +- SourceGen/RuntimeData/Apple/ProDOS8.cs | 4 +- SourceGen/RuntimeData/Apple/VisHiRes.cs | 76 +++++++ SourceGen/Sandbox/ScriptManager.cs | 24 ++- SourceGen/SourceGen.csproj | 8 + SourceGen/Visualization.cs | 108 ++++++++++ SourceGen/VisualizationSet.cs | 60 +++++- SourceGen/WpfGui/EditVisualization.xaml | 120 +++++++++++ SourceGen/WpfGui/EditVisualization.xaml.cs | 194 ++++++++++++++++++ SourceGen/WpfGui/EditVisualizationSet.xaml | 50 ++++- SourceGen/WpfGui/EditVisualizationSet.xaml.cs | 75 +++++-- 14 files changed, 747 insertions(+), 32 deletions(-) create mode 100644 SourceGen/RuntimeData/Apple/VisHiRes.cs create mode 100644 SourceGen/Visualization.cs create mode 100644 SourceGen/WpfGui/EditVisualization.xaml create mode 100644 SourceGen/WpfGui/EditVisualization.xaml.cs diff --git a/PluginCommon/Interfaces.cs b/PluginCommon/Interfaces.cs index e5dc835..724f234 100644 --- a/PluginCommon/Interfaces.cs +++ b/PluginCommon/Interfaces.cs @@ -123,6 +123,55 @@ namespace PluginCommon { void CheckBrk(int offset, bool isTwoBytes, out bool noContinue); } + /// + /// Extension scripts that want to generate 2D visualizations must implement this interface. + /// + public interface IPlugin_Visualizer2d { + string[] GetVisGenNames(); + + List GetVisGenParams(string name); + + IVisualization2d ExecuteVisGen(string name, Dictionary parms); + } + + /// + /// Visualization parameter descriptor. + /// + [Serializable] + public class VisParamDescr { + public enum SpecialMode { + None = 0, Offset + } + + public string UiLabel { get; private set; } + public string Name { get; private set; } + public Type CsType { get; private set; } + public object Min { get; private set; } + public object Max { get; private set; } + public SpecialMode Special { get; private set; } + public object DefaultValue { get; private set; } + + public VisParamDescr(string uiLabel, string name, Type csType, object min, object max, + SpecialMode special, object defVal) { + UiLabel = uiLabel; + Name = name; + CsType = csType; + Min = min; + Max = max; + Special = special; + DefaultValue = defVal; + } + } + + /// + /// Rendered 2D visualization object. + /// + public interface IVisualization2d { + int GetWidth(); + int GetHeight(); + int GetPixel(int x, int y); // returns ARGB value + } + /// /// Interfaces provided by the application for use by plugins. An IApplication instance /// is passed to the plugin as an argument Prepare(). diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index 9e6b216..d3f7aeb 100644 --- a/SourceGen/DisasmProject.cs +++ b/SourceGen/DisasmProject.cs @@ -2443,6 +2443,11 @@ namespace SourceGen { return bestSym; } + public PluginCommon.IPlugin GetMatchingScript(ScriptManager.CheckMatch check) { + return mScriptManager.GetMatchingScript(check); + } + + /// /// For debugging purposes, get some information about the currently loaded /// extension scripts. diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index 5c1e5ff..79b3812 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -510,7 +510,7 @@ namespace SourceGen { mProject.VisualizationSets.TryGetValue(line.FileOffset, out VisualizationSet visSet); parts = FormattedParts.CreateLongComment("!VISUALIZATION SET! " + - (visSet != null ? visSet.PlaceHolder : "???")); + (visSet != null ? "VS:" + visSet.Count : "???")); break; case Line.Type.Blank: // Nothing to do. diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index fcf4803..25c960a 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -2160,8 +2160,8 @@ namespace SourceGen { int offset = CodeLineList[selIndex].FileOffset; mProject.VisualizationSets.TryGetValue(offset, out VisualizationSet curVisSet); - EditVisualizationSet dlg = new EditVisualizationSet(mMainWin, - curVisSet); + EditVisualizationSet dlg = new EditVisualizationSet(mMainWin, mProject, + mOutputFormatter, curVisSet); if (dlg.ShowDialog() != true) { return; } diff --git a/SourceGen/RuntimeData/Apple/ProDOS8.cs b/SourceGen/RuntimeData/Apple/ProDOS8.cs index 6c55a22..b2f67ce 100644 --- a/SourceGen/RuntimeData/Apple/ProDOS8.cs +++ b/SourceGen/RuntimeData/Apple/ProDOS8.cs @@ -156,9 +156,7 @@ namespace RuntimeData.Apple { private AddressTranslate mAddrTrans; public string Identifier { - get { - return "Apple II ProDOS 8 MLI call handler"; - } + get { return "Apple II ProDOS 8 MLI call handler"; } } public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { diff --git a/SourceGen/RuntimeData/Apple/VisHiRes.cs b/SourceGen/RuntimeData/Apple/VisHiRes.cs new file mode 100644 index 0000000..7b911ff --- /dev/null +++ b/SourceGen/RuntimeData/Apple/VisHiRes.cs @@ -0,0 +1,76 @@ +/* + * Copyright 2018 faddenSoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using PluginCommon; + +namespace RuntimeData.Apple { + public class VisHiRes : MarshalByRefObject, IPlugin, IPlugin_Visualizer2d { + // Visualization identifiers; DO NOT change. + private const string VIS_GEN_BITMAP = "apple2-hi-res-bitmap"; + + public string Identifier { + get { return "Apple II Hi-Res Graphic Visualizer"; } + } + + private IApplication mAppRef; + private byte[] mFileData; + private AddressTranslate mAddrTrans; + + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { + mAppRef = appRef; + mFileData = fileData; + mAddrTrans = addrTrans; + } + + public string[] GetVisGenNames() { + return new string[] { + VIS_GEN_BITMAP, + }; + } + + public List GetVisGenParams(string name) { + List parms = new List(); + + switch (name) { + case VIS_GEN_BITMAP: + parms.Add(new VisParamDescr("File Offset", + "offset", typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, + 0x2000)); + parms.Add(new VisParamDescr("Width (bytes)", + "byteWidth", typeof(int), 1, 40, 0, 1)); + parms.Add(new VisParamDescr("Height", + "height", typeof(int), 1, 192, 0, 1)); + parms.Add(new VisParamDescr("Color", + "color", typeof(bool), 0, 0, 0, true)); + parms.Add(new VisParamDescr("Test Float", + "floaty", typeof(float), -5.0f, 5.0f, 0, 0.1f)); + break; + default: + parms = null; + break; + } + + return parms; + } + + public IVisualization2d ExecuteVisGen(string name, Dictionary parms) { + throw new NotImplementedException(); + } + } +} diff --git a/SourceGen/Sandbox/ScriptManager.cs b/SourceGen/Sandbox/ScriptManager.cs index 9970a32..c1a87b9 100644 --- a/SourceGen/Sandbox/ScriptManager.cs +++ b/SourceGen/Sandbox/ScriptManager.cs @@ -256,6 +256,25 @@ namespace SourceGen.Sandbox { return plSymbols; } + public delegate bool CheckMatch(IPlugin plugin); + public IPlugin GetMatchingScript(CheckMatch check) { + if (DomainMgr == null) { + foreach (KeyValuePair kvp in mActivePlugins) { + if (check(kvp.Value)) { + return kvp.Value; + } + } + } else { + List plugins = DomainMgr.PluginMgr.GetActivePlugins(); + foreach (IPlugin plugin in plugins) { + if (check(plugin)) { + return plugin; + } + } + } + return null; + } + /// /// For debugging purposes, get some information about the currently loaded /// extension scripts. @@ -289,7 +308,7 @@ namespace SourceGen.Sandbox { // The plugin is actually a MarshalByRefObject, so we can't use reflection // to gather the list of interfaces. - // TODO(maybe): add a call that does the query on the remote site + // TODO(maybe): add a call that does a reflection query on the remote side if (plugin is PluginCommon.IPlugin_SymbolList) { sb.Append(" SymbolList"); } @@ -302,6 +321,9 @@ namespace SourceGen.Sandbox { if (plugin is PluginCommon.IPlugin_InlineBrk) { sb.Append(" InlineBrk"); } + if (plugin is PluginCommon.IPlugin_Visualizer2d) { + sb.Append(" Visualizer2d"); + } sb.Append("\r\n"); } } diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj index 9c76a6d..2dc19cc 100644 --- a/SourceGen/SourceGen.csproj +++ b/SourceGen/SourceGen.csproj @@ -96,6 +96,7 @@ ShowText.xaml + AboutBox.xaml @@ -133,6 +134,9 @@ EditProjectProperties.xaml + + EditVisualization.xaml + EditVisualizationSet.xaml @@ -314,6 +318,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/SourceGen/Visualization.cs b/SourceGen/Visualization.cs new file mode 100644 index 0000000..bf451a1 --- /dev/null +++ b/SourceGen/Visualization.cs @@ -0,0 +1,108 @@ +/* + * Copyright 2019 faddenSoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using PluginCommon; + +namespace SourceGen { + public class Visualization { + /// + /// Unique tag. Contents are arbitrary, but may not be empty. + /// + public string Tag { get; private set; } + + /// + /// Name of visualization generator (extension script function). + /// + public string VisGenName { get; private set; } + + /// + /// Parameters passed to the visualization generator. + /// + public Dictionary VisGenParams { get; private set; } + + public double Thumbnail { get; } // TODO - 64x64(?) bitmap + + + /// + /// Constructor. + /// + /// + /// + /// + public Visualization(string tag, string visGenName, + Dictionary visGenParams) { + Tag = tag; + VisGenName = visGenName; + VisGenParams = visGenParams; + } + + + /// + /// Finds a plugin that provides the named visualization generator. + /// + /// Project with script manager. + /// Visualization generator name. + /// A plugin that matches, or null if none found. + public static IPlugin_Visualizer2d FindPluginByVisGenName(DisasmProject proj, + string visGenName) { + Sandbox.ScriptManager.CheckMatch check = (chkPlug) => { + if (!(chkPlug is IPlugin_Visualizer2d)) { + return false; + } + IPlugin_Visualizer2d vplug = (IPlugin_Visualizer2d)chkPlug; + string[] names = vplug.GetVisGenNames(); + foreach (string name in names) { + if (name == visGenName) { + return true; + } + } + return false; + }; + return (IPlugin_Visualizer2d)proj.GetMatchingScript(check); + } + + + public override string ToString() { + return "[Vis: " + Tag + " (" + VisGenName + ")]"; + } + + public static bool operator ==(Visualization a, Visualization b) { + if (ReferenceEquals(a, b)) { + return true; // same object, or both null + } + if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) { + return false; // one is null + } + // All fields must be equal. + if (a.Tag != b.Tag || a.VisGenName != b.VisGenName || a.Thumbnail != b.Thumbnail) { + return false; + } + return a.VisGenParams != b.VisGenParams; // TODO(xyzzy): should be item-by-item + } + public static bool operator !=(Visualization a, Visualization b) { + return !(a == b); + } + public override bool Equals(object obj) { + return obj is Visualization && this == (Visualization)obj; + } + public override int GetHashCode() { + return Tag.GetHashCode() ^ VisGenName.GetHashCode() ^ VisGenParams.Count; + } + } +} diff --git a/SourceGen/VisualizationSet.cs b/SourceGen/VisualizationSet.cs index 2b11730..50e9f72 100644 --- a/SourceGen/VisualizationSet.cs +++ b/SourceGen/VisualizationSet.cs @@ -14,23 +14,50 @@ * limitations under the License. */ using System; +using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; namespace SourceGen { - public class VisualizationSet { - // TODO(xyzzy) - public string PlaceHolder { get; private set; } + public class VisualizationSet : IEnumerable { + /// + /// Ordered list of visualization objects. + /// + private List mList; - public VisualizationSet(string placeHolder) { - PlaceHolder = placeHolder; + + /// + /// Constructor. + /// + /// Initial capacity. + public VisualizationSet(int cap = 1) { + mList = new List(cap); + } + + // IEnumerable + public IEnumerator GetEnumerator() { + return mList.GetEnumerator(); + } + + // IEnumerable + IEnumerator IEnumerable.GetEnumerator() { + return mList.GetEnumerator(); + } + + /// + /// The number of entries in the table. + /// + public int Count { + get { return mList.Count; } + } + + public void Add(Visualization vis) { + mList.Add(vis); } public override string ToString() { - return "[VS: " + PlaceHolder + "]"; + return "[VS: " + mList.Count + " items]"; } public static bool operator ==(VisualizationSet a, VisualizationSet b) { @@ -41,7 +68,16 @@ namespace SourceGen { return false; // one is null } // All fields must be equal. - return a.PlaceHolder == b.PlaceHolder; + if (a.mList.Count != b.mList.Count) { + return false; + } + // Order matters. + for (int i = 0; i < a.mList.Count; i++) { + if (a.mList[i] != b.mList[i]) { + return false; + } + } + return true; } public static bool operator !=(VisualizationSet a, VisualizationSet b) { return !(a == b); @@ -50,7 +86,11 @@ namespace SourceGen { return obj is VisualizationSet && this == (VisualizationSet)obj; } public override int GetHashCode() { - return PlaceHolder.GetHashCode(); + int hashCode = 0; + foreach (Visualization vis in mList) { + hashCode ^= vis.GetHashCode(); + } + return hashCode; } } } diff --git a/SourceGen/WpfGui/EditVisualization.xaml b/SourceGen/WpfGui/EditVisualization.xaml new file mode 100644 index 0000000..54483d6 --- /dev/null +++ b/SourceGen/WpfGui/EditVisualization.xaml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +