mirror of
https://github.com/fadden/6502bench.git
synced 2024-12-26 19:29:17 +00:00
More progress on visualization
Got parameter in/out working in EditVisualization dialog. Did some rearranging in PluginCommon interfaces and data structures. Still doesn't do anything useful.
This commit is contained in:
parent
836626bdc3
commit
e7fccfda03
@ -36,5 +36,44 @@ namespace CommonUtil {
|
||||
}
|
||||
return Enumerable.SequenceEqual<string>(l1, l2, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two Dictionaries to see if their contents are equal. Key and value types
|
||||
/// must have correctly-implemented equality checks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// https://stackoverflow.com/q/3804367/294248
|
||||
/// </remarks>
|
||||
/// <typeparam name="TKey">Dictionary key type.</typeparam>
|
||||
/// <typeparam name="TValue">Dictionary value type.</typeparam>
|
||||
/// <param name="dict1">Dictionary #1.</param>
|
||||
/// <param name="dict2">Dictionary #2.</param>
|
||||
/// <returns>True if equal, false if not.</returns>
|
||||
public static bool CompareDicts<TKey, TValue>(
|
||||
Dictionary<TKey, TValue> dict1, Dictionary<TKey, TValue> dict2) {
|
||||
if (dict1 == dict2) {
|
||||
return true;
|
||||
}
|
||||
if (dict1 == null || dict2 == null) {
|
||||
return false;
|
||||
}
|
||||
if (dict1.Count != dict2.Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if false
|
||||
var valueComparer = EqualityComparer<TValue>.Default;
|
||||
|
||||
foreach (var kvp in dict1) {
|
||||
TValue value2;
|
||||
if (!dict2.TryGetValue(kvp.Key, out value2)) return false;
|
||||
if (!valueComparer.Equals(kvp.Value, value2)) return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
// Check to see if there are any elements in the first that are not in the second.
|
||||
return !dict1.Except(dict2).Any();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -124,14 +124,58 @@ namespace PluginCommon {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension scripts that want to generate 2D visualizations must implement this interface.
|
||||
/// Extension scripts that want to generate visualizations must implement this interface.
|
||||
/// </summary>
|
||||
public interface IPlugin_Visualizer2d {
|
||||
string[] GetVisGenNames();
|
||||
public interface IPlugin_Visualizer {
|
||||
/// <summary>
|
||||
/// Retrieves a list of descriptors for visualization generators implemented by this
|
||||
/// plugin. The caller must not modify the contents.
|
||||
/// </summary>
|
||||
VisDescr[] GetVisGenDescrs();
|
||||
|
||||
List<VisParamDescr> GetVisGenParams(string name);
|
||||
/// <summary>
|
||||
/// Executes the specified visualization generator with the supplied parameters.
|
||||
/// </summary>
|
||||
/// <param name="descr">VisGen identifier.</param>
|
||||
/// <param name="parms">Parameter set.</param>
|
||||
/// <returns>2D visualization object reference.</returns>
|
||||
IVisualization2d Generate2d(VisDescr descr, Dictionary<string, object> parms);
|
||||
}
|
||||
|
||||
IVisualization2d ExecuteVisGen(string name, Dictionary<string, object> parms);
|
||||
[Serializable]
|
||||
public class VisDescr {
|
||||
/// <summary>
|
||||
/// Unique identifier. This is stored in the project file.
|
||||
/// </summary>
|
||||
public string Ident { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable string describing the visualizer. Used for combo boxes and
|
||||
/// other UI elements.
|
||||
/// </summary>
|
||||
public string UiName { get; private set; }
|
||||
|
||||
public enum VisType {
|
||||
Unknown = 0,
|
||||
Bitmap, // 2D bitmap
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visualization type.
|
||||
/// </summary>
|
||||
public VisType VisualizationType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Visualization parameter descriptors.
|
||||
/// </summary>
|
||||
public VisParamDescr[] VisParamDescrs { get; private set; }
|
||||
|
||||
public VisDescr(string ident, string uiName, VisType type, VisParamDescr[] descrs) {
|
||||
Ident = ident;
|
||||
UiName = uiName;
|
||||
VisualizationType = type;
|
||||
VisParamDescrs = descrs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -146,6 +190,10 @@ namespace PluginCommon {
|
||||
public string UiLabel { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public Type CsType { get; private set; }
|
||||
|
||||
// Min/Max values for ints and floats. Could also be length for strings.
|
||||
// NOTE: ideally we'd provide a "verify" function that tested individual fields and
|
||||
// could also see other fields, e.g. to confirm that Stride >= Width.
|
||||
public object Min { get; private set; }
|
||||
public object Max { get; private set; }
|
||||
public SpecialMode Special { get; private set; }
|
||||
@ -164,12 +212,15 @@ namespace PluginCommon {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rendered 2D visualization object.
|
||||
/// Rendered 2D visualization. The object represents the "raw" form of the data,
|
||||
/// without scaling or filtering.
|
||||
/// </summary>
|
||||
public interface IVisualization2d {
|
||||
int GetWidth();
|
||||
int GetHeight();
|
||||
int GetPixel(int x, int y); // returns ARGB value
|
||||
|
||||
// TODO(maybe): pixel aspect ratio
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -2443,11 +2443,10 @@ namespace SourceGen {
|
||||
return bestSym;
|
||||
}
|
||||
|
||||
public PluginCommon.IPlugin GetMatchingScript(ScriptManager.CheckMatch check) {
|
||||
return mScriptManager.GetMatchingScript(check);
|
||||
public List<PluginCommon.IPlugin> GetActivePlugins() {
|
||||
return mScriptManager.GetActivePlugins();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// For debugging purposes, get some information about the currently loaded
|
||||
/// extension scripts.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018 faddenSoft
|
||||
* 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.
|
||||
@ -20,56 +20,69 @@ 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 class VisHiRes : MarshalByRefObject, IPlugin, IPlugin_Visualizer {
|
||||
public string Identifier {
|
||||
get { return "Apple II Hi-Res Graphic Visualizer"; }
|
||||
}
|
||||
|
||||
private IApplication mAppRef;
|
||||
private byte[] mFileData;
|
||||
private AddressTranslate mAddrTrans;
|
||||
|
||||
// Visualization identifiers; DO NOT change or projects will break.
|
||||
private const string VIS_GEN_BITMAP = "apple2-hi-res-bitmap";
|
||||
private const string VIS_GEN_MULTI_MAP = "apple2-hi-res-multi-map";
|
||||
|
||||
// Visualization descriptors.
|
||||
private VisDescr[] mDescriptors = new VisDescr[] {
|
||||
new VisDescr(VIS_GEN_BITMAP, "Apple II Hi-Res Bitmap", VisDescr.VisType.Bitmap,
|
||||
new VisParamDescr[] {
|
||||
new VisParamDescr("File offset (hex)",
|
||||
"offset", typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset,
|
||||
0x2000),
|
||||
new VisParamDescr("Width (in bytes)",
|
||||
"byteWidth", typeof(int), 1, 40, 0, 1),
|
||||
new VisParamDescr("Height",
|
||||
"height", typeof(int), 1, 192, 0, 1),
|
||||
new VisParamDescr("Column stride (bytes)",
|
||||
"colStride", typeof(int), 0, 256, 0, 0),
|
||||
new VisParamDescr("Row stride (bytes)",
|
||||
"rowStride", typeof(int), 0, 256, 0, 0),
|
||||
new VisParamDescr("Color",
|
||||
"color", typeof(bool), 0, 0, 0, true),
|
||||
new VisParamDescr("First byte odd",
|
||||
"firstOdd", typeof(bool), 0, 0, 0, false),
|
||||
new VisParamDescr("Test Float",
|
||||
"floaty", typeof(float), -5.0f, 5.0f, 0, 0.1f),
|
||||
}),
|
||||
new VisDescr(VIS_GEN_MULTI_MAP, "Apple II Hi-Res Multi-Map", VisDescr.VisType.Bitmap,
|
||||
new VisParamDescr[] {
|
||||
new VisParamDescr("File offset (hex)",
|
||||
"offset", typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset,
|
||||
0x1000),
|
||||
new VisParamDescr("Item width (in bytes)",
|
||||
"itemByteWidth", typeof(int), 1, 40, 0, 1),
|
||||
new VisParamDescr("Item height",
|
||||
"itemHeight", typeof(int), 8, 192, 0, 1),
|
||||
new VisParamDescr("Number of items",
|
||||
"count", typeof(int), 1, 256, 0, 1),
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) {
|
||||
mAppRef = appRef;
|
||||
mFileData = fileData;
|
||||
mAddrTrans = addrTrans;
|
||||
}
|
||||
|
||||
public string[] GetVisGenNames() {
|
||||
return new string[] {
|
||||
VIS_GEN_BITMAP,
|
||||
};
|
||||
// IPlugin_Visualizer
|
||||
public VisDescr[] GetVisGenDescrs() {
|
||||
return mDescriptors;
|
||||
}
|
||||
|
||||
public List<VisParamDescr> GetVisGenParams(string name) {
|
||||
List<VisParamDescr> parms = new List<VisParamDescr>();
|
||||
|
||||
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<string, object> parms) {
|
||||
// IPlugin_Visualizer
|
||||
public IVisualization2d Generate2d(VisDescr descr,
|
||||
Dictionary<string, object> parms) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
@ -275,6 +275,22 @@ namespace SourceGen.Sandbox {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of loaded plugins. Callers should not retain this list, as the
|
||||
/// set can change due to user activity.
|
||||
/// </summary>
|
||||
public List<IPlugin> GetActivePlugins() {
|
||||
if (DomainMgr == null) {
|
||||
List<IPlugin> plist = new List<IPlugin>();
|
||||
foreach (KeyValuePair<string, IPlugin> kvp in mActivePlugins) {
|
||||
plist.Add(kvp.Value);
|
||||
}
|
||||
return plist;
|
||||
} else {
|
||||
return DomainMgr.PluginMgr.GetActivePlugins();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For debugging purposes, get some information about the currently loaded
|
||||
/// extension scripts.
|
||||
@ -321,8 +337,8 @@ namespace SourceGen.Sandbox {
|
||||
if (plugin is PluginCommon.IPlugin_InlineBrk) {
|
||||
sb.Append(" InlineBrk");
|
||||
}
|
||||
if (plugin is PluginCommon.IPlugin_Visualizer2d) {
|
||||
sb.Append(" Visualizer2d");
|
||||
if (plugin is PluginCommon.IPlugin_Visualizer) {
|
||||
sb.Append(" Visualizer");
|
||||
}
|
||||
sb.Append("\r\n");
|
||||
}
|
||||
|
@ -17,19 +17,20 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using CommonUtil;
|
||||
using PluginCommon;
|
||||
|
||||
namespace SourceGen {
|
||||
public class Visualization {
|
||||
/// <summary>
|
||||
/// Unique tag. Contents are arbitrary, but may not be empty.
|
||||
/// Unique user-specified tag. Contents are arbitrary, but may not be empty.
|
||||
/// </summary>
|
||||
public string Tag { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of visualization generator (extension script function).
|
||||
/// </summary>
|
||||
public string VisGenName { get; private set; }
|
||||
public string VisGenIdent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parameters passed to the visualization generator.
|
||||
@ -43,12 +44,12 @@ namespace SourceGen {
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="visGenName"></param>
|
||||
/// <param name="visGenIdent"></param>
|
||||
/// <param name="visGenParams"></param>
|
||||
public Visualization(string tag, string visGenName,
|
||||
public Visualization(string tag, string visGenIdent,
|
||||
Dictionary<string, object> visGenParams) {
|
||||
Tag = tag;
|
||||
VisGenName = visGenName;
|
||||
VisGenIdent = visGenIdent;
|
||||
VisGenParams = visGenParams;
|
||||
}
|
||||
|
||||
@ -57,29 +58,30 @@ namespace SourceGen {
|
||||
/// Finds a plugin that provides the named visualization generator.
|
||||
/// </summary>
|
||||
/// <param name="proj">Project with script manager.</param>
|
||||
/// <param name="visGenName">Visualization generator name.</param>
|
||||
/// <param name="visGenIdent">Visualization generator identifier.</param>
|
||||
/// <returns>A plugin that matches, or null if none found.</returns>
|
||||
public static IPlugin_Visualizer2d FindPluginByVisGenName(DisasmProject proj,
|
||||
string visGenName) {
|
||||
Sandbox.ScriptManager.CheckMatch check = (chkPlug) => {
|
||||
if (!(chkPlug is IPlugin_Visualizer2d)) {
|
||||
return false;
|
||||
public static IPlugin_Visualizer FindPluginByVisGenIdent(DisasmProject proj,
|
||||
string visGenIdent, out VisDescr visDescr) {
|
||||
List<IPlugin> plugins = proj.GetActivePlugins();
|
||||
foreach (IPlugin chkPlug in plugins) {
|
||||
if (!(chkPlug is IPlugin_Visualizer)) {
|
||||
continue;
|
||||
}
|
||||
IPlugin_Visualizer2d vplug = (IPlugin_Visualizer2d)chkPlug;
|
||||
string[] names = vplug.GetVisGenNames();
|
||||
foreach (string name in names) {
|
||||
if (name == visGenName) {
|
||||
return true;
|
||||
IPlugin_Visualizer vplug = (IPlugin_Visualizer)chkPlug;
|
||||
foreach (VisDescr descr in vplug.GetVisGenDescrs()) {
|
||||
if (descr.Ident == visGenIdent) {
|
||||
visDescr = descr;
|
||||
return vplug;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return (IPlugin_Visualizer2d)proj.GetMatchingScript(check);
|
||||
}
|
||||
visDescr = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString() {
|
||||
return "[Vis: " + Tag + " (" + VisGenName + ")]";
|
||||
return "[Vis: " + Tag + " (" + VisGenIdent + ") count=" + VisGenParams.Count + "]";
|
||||
}
|
||||
|
||||
public static bool operator ==(Visualization a, Visualization b) {
|
||||
@ -90,10 +92,17 @@ namespace SourceGen {
|
||||
return false; // one is null
|
||||
}
|
||||
// All fields must be equal.
|
||||
if (a.Tag != b.Tag || a.VisGenName != b.VisGenName || a.Thumbnail != b.Thumbnail) {
|
||||
if (a.Tag != b.Tag || a.VisGenIdent != b.VisGenIdent || a.Thumbnail != b.Thumbnail) {
|
||||
return false;
|
||||
}
|
||||
return a.VisGenParams != b.VisGenParams; // TODO(xyzzy): should be item-by-item
|
||||
// Compare the vis gen parameter lists.
|
||||
if (a.VisGenParams == b.VisGenParams) {
|
||||
return true;
|
||||
}
|
||||
if (a.VisGenParams.Count != b.VisGenParams.Count) {
|
||||
return false;
|
||||
}
|
||||
return Container.CompareDicts(a.VisGenParams, b.VisGenParams);
|
||||
}
|
||||
public static bool operator !=(Visualization a, Visualization b) {
|
||||
return !(a == b);
|
||||
@ -102,7 +111,8 @@ namespace SourceGen {
|
||||
return obj is Visualization && this == (Visualization)obj;
|
||||
}
|
||||
public override int GetHashCode() {
|
||||
return Tag.GetHashCode() ^ VisGenName.GetHashCode() ^ VisGenParams.Count;
|
||||
// TODO(maybe): hash code should include up VisGenParams items
|
||||
return Tag.GetHashCode() ^ VisGenIdent.GetHashCode() ^ VisGenParams.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,12 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SourceGen {
|
||||
/// <summary>
|
||||
/// Ordered list of visualization objects.
|
||||
/// </summary>
|
||||
public class VisualizationSet : IEnumerable<Visualization> {
|
||||
/// <summary>
|
||||
/// Ordered list of visualization objects.
|
||||
/// Object list.
|
||||
/// </summary>
|
||||
private List<Visualization> mList;
|
||||
|
||||
|
@ -89,6 +89,9 @@ namespace SourceGen.WpfGui {
|
||||
/// </summary>
|
||||
private AssemblerInfo.Id mInitialAsmId;
|
||||
|
||||
/// <summary>
|
||||
/// List of assemblers, for combo boxes.
|
||||
/// </summary>
|
||||
public List<AssemblerInfo> AssemblerList { get; private set; }
|
||||
|
||||
|
||||
|
@ -47,8 +47,8 @@ namespace SourceGen.WpfGui {
|
||||
/// </summary>
|
||||
private int mCurrentOffset;
|
||||
|
||||
// Dialog label text color, saved off at dialog load time.
|
||||
private Brush mDefaultLabelColor = Brushes.Black;
|
||||
// Dialog label text color.
|
||||
private Brush mDefaultLabelColor = SystemColors.WindowTextBrush;
|
||||
private Brush mErrorLabelColor = Brushes.Red;
|
||||
|
||||
public string OffsetStr {
|
||||
|
@ -30,29 +30,53 @@ limitations under the License.
|
||||
<Window.Resources>
|
||||
<!-- big thanks: http://drwpf.com/blog/2008/01/03/itemscontrol-d-is-for-datatemplate/ -->
|
||||
<DataTemplate x:Key="BoolTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Width="100" Text="{Binding UiName}"/>
|
||||
<CheckBox IsChecked="{Binding Value}"/>
|
||||
<TextBlock Text="{Binding RangeText}" FontFamily="{StaticResource GeneralMonoFont}"/>
|
||||
</StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="130"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,4,0"
|
||||
Text="{Binding UiString}" Foreground="{Binding ForegroundBrush}"/>
|
||||
<CheckBox Grid.Column="1" Margin="0,0,0,4" IsChecked="{Binding Value}"
|
||||
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
|
||||
<TextBlock Grid.Column="2" Text="{Binding RangeText}" Margin="0,1,0,0"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="IntTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Width="100" Text="{Binding UiName}"/>
|
||||
<TextBox Width="100" MaxLength="11" Text="{Binding Value}"
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="130"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,4,0"
|
||||
Text="{Binding UiString}" Foreground="{Binding ForegroundBrush}"/>
|
||||
<TextBox Grid.Column="1" MaxLength="11" Margin="0,1,0,4"
|
||||
Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
<TextBlock Text="{Binding RangeText}" FontFamily="{StaticResource GeneralMonoFont}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="2" Text="{Binding RangeText}" Margin="0,1,0,0"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="FloatTemplate">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Width="100" Text="{Binding UiName}"/>
|
||||
<TextBox Width="100" MaxLength="11" Text="{Binding Value}"
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="130"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" HorizontalAlignment="Right" Margin="0,0,4,0"
|
||||
Text="{Binding UiString}" Foreground="{Binding ForegroundBrush}"/>
|
||||
<TextBox Grid.Column="1" MaxLength="11" Margin="0,1,0,4"
|
||||
Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"
|
||||
TextChanged="TextBox_TextChanged"/>
|
||||
<TextBlock Text="{Binding RangeText}" FontFamily="{StaticResource GeneralMonoFont}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="2" Text="{Binding RangeText}" Margin="0,1,0,0"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- define and configure the template selector, which chooses one of the above
|
||||
@ -73,25 +97,31 @@ limitations under the License.
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="130"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Margin="0,0,4,0"
|
||||
Text="Visualizer:"/>
|
||||
<ComboBox Grid.Column="1" Grid.Row="0" />
|
||||
<ComboBox Name="visComboBox" Grid.Column="1" Grid.Row="0" Margin="0,0,0,4"
|
||||
ItemsSource="{Binding VisualizationList}" DisplayMemberPath="UiName"
|
||||
SelectionChanged="VisComboBox_SelectionChanged"/>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="0,0,4,0"
|
||||
Text="Tag:"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="1" Width="300"
|
||||
Text="{Binding TagString, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
Text="Tag:" Foreground="{Binding TagLabelBrush}"/>
|
||||
<TextBox Grid.Column="1" Grid.Row="1"
|
||||
Text="{Binding TagString, UpdateSourceTrigger=PropertyChanged}"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"/>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Margin="0,20,0,0" Text="Preview:"/>
|
||||
<TextBlock Grid.Column="1" Grid.Row="2" Text="• Must be unique, 2+ chars"/>
|
||||
|
||||
<TextBlock Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" Margin="0,20,0,4" Text="Preview:"/>
|
||||
</Grid>
|
||||
|
||||
<Image Grid.Row="1" Width="50" Height="50"/>
|
||||
@ -102,7 +132,7 @@ limitations under the License.
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="Parameters:"/>
|
||||
<TextBlock Grid.Row="0" Text="Parameters:" Margin="0,0,0,4"/>
|
||||
|
||||
<!-- generated controls are placed here -->
|
||||
<ItemsControl Grid.Row="1"
|
||||
@ -114,7 +144,7 @@ limitations under the License.
|
||||
<DockPanel Grid.Column="0" Grid.Row="3" Margin="0,8,0,0" LastChildFill="False">
|
||||
<Button DockPanel.Dock="Right" Content="Cancel" Width="70" Margin="8,0,0,0" IsCancel="True"/>
|
||||
<Button DockPanel.Dock="Right" Grid.Column="1" Content="OK" Width="70"
|
||||
IsDefault="True" Click="OkButton_Click"/>
|
||||
IsDefault="True" IsEnabled="{Binding IsValid}" Click="OkButton_Click"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
@ -16,6 +16,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
@ -33,20 +34,56 @@ namespace SourceGen.WpfGui {
|
||||
/// Visualization editor.
|
||||
/// </summary>
|
||||
public partial class EditVisualization : Window, INotifyPropertyChanged {
|
||||
private const int MIN_TRIMMED_TAG_LEN = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Dialog result.
|
||||
/// </summary>
|
||||
public Visualization NewVis { get; private set; }
|
||||
|
||||
private DisasmProject mProject;
|
||||
private Formatter mFormatter;
|
||||
private Visualization mOrigVis;
|
||||
|
||||
private Brush mDefaultLabelColor = SystemColors.WindowTextBrush;
|
||||
private Brush mErrorLabelColor = Brushes.Red;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// True if all properties are in valid ranges. Determines whether the OK button
|
||||
/// is enabled.
|
||||
/// </summary>
|
||||
public bool IsValid {
|
||||
get { return mIsValid; }
|
||||
set { mIsValid = value; OnPropertyChanged(); }
|
||||
}
|
||||
private bool mIsValid;
|
||||
|
||||
/// <summary>
|
||||
/// Visualization tag.
|
||||
/// </summary>
|
||||
public string TagString {
|
||||
get { return mTagString; }
|
||||
set { mTagString = value; OnPropertyChanged(); }
|
||||
}
|
||||
private string mTagString;
|
||||
|
||||
public IList<ParameterValue> ParameterList {
|
||||
get { return mParameterList; }
|
||||
public Brush TagLabelBrush {
|
||||
get { return mTagLabelBrush; }
|
||||
set { mTagLabelBrush = value; OnPropertyChanged(); }
|
||||
}
|
||||
private List<ParameterValue> mParameterList;
|
||||
private Brush mTagLabelBrush;
|
||||
|
||||
/// <summary>
|
||||
/// ItemsSource for the ItemsControl with the generated parameter controls.
|
||||
/// </summary>
|
||||
public ObservableCollection<ParameterValue> ParameterList { get; private set; } =
|
||||
new ObservableCollection<ParameterValue>();
|
||||
|
||||
/// <summary>
|
||||
/// List of visualizers, for combo box.
|
||||
/// </summary>
|
||||
public List<VisDescr> VisualizationList { get; private set; }
|
||||
|
||||
// INotifyPropertyChanged implementation
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
@ -54,6 +91,14 @@ namespace SourceGen.WpfGui {
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="owner">Owner window.</param>
|
||||
/// <param name="proj">Project reference.</param>
|
||||
/// <param name="formatter">Text formatter.</param>
|
||||
/// <param name="vis">Visualization to edit, or null if this is new.</param>
|
||||
public EditVisualization(Window owner, DisasmProject proj, Formatter formatter,
|
||||
Visualization vis) {
|
||||
InitializeComponent();
|
||||
@ -64,12 +109,28 @@ namespace SourceGen.WpfGui {
|
||||
mFormatter = formatter;
|
||||
mOrigVis = vis;
|
||||
|
||||
// TODO: configure ComboBox from vis arg if non-null, then use current
|
||||
// combo box selection, updating in selchange event
|
||||
string visGenName = "apple2-hi-res-bitmap";
|
||||
if (vis != null) {
|
||||
TagString = vis.Tag;
|
||||
}
|
||||
|
||||
mParameterList = new List<ParameterValue>();
|
||||
GenerateParamControls(visGenName);
|
||||
int visSelection = 0;
|
||||
VisualizationList = new List<VisDescr>();
|
||||
List<IPlugin> plugins = proj.GetActivePlugins();
|
||||
foreach (IPlugin chkPlug in plugins) {
|
||||
if (!(chkPlug is IPlugin_Visualizer)) {
|
||||
continue;
|
||||
}
|
||||
IPlugin_Visualizer vplug = (IPlugin_Visualizer)chkPlug;
|
||||
foreach (VisDescr descr in vplug.GetVisGenDescrs()) {
|
||||
if (vis != null && vis.VisGenIdent == descr.Ident) {
|
||||
visSelection = VisualizationList.Count;
|
||||
}
|
||||
VisualizationList.Add(descr);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the selection. This should cause the sel change event to fire.
|
||||
visComboBox.SelectedIndex = visSelection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -81,13 +142,11 @@ namespace SourceGen.WpfGui {
|
||||
/// If we don't find a corresponding entry in the Visualization, we use the
|
||||
/// default value.
|
||||
/// </remarks>
|
||||
private void GenerateParamControls(string visGenName) {
|
||||
IPlugin_Visualizer2d plugin =
|
||||
Visualization.FindPluginByVisGenName(mProject, visGenName);
|
||||
List<VisParamDescr> descrs = plugin.GetVisGenParams(visGenName);
|
||||
private void GenerateParamControls(VisDescr descr) {
|
||||
VisParamDescr[] paramDescrs = descr.VisParamDescrs;
|
||||
|
||||
mParameterList.Clear();
|
||||
foreach (VisParamDescr vpd in descrs) {
|
||||
ParameterList.Clear();
|
||||
foreach (VisParamDescr vpd in paramDescrs) {
|
||||
string rangeStr = string.Empty;
|
||||
object defaultVal = vpd.DefaultValue;
|
||||
if (mOrigVis.VisGenParams.TryGetValue(vpd.Name, out object val)) {
|
||||
@ -95,20 +154,25 @@ namespace SourceGen.WpfGui {
|
||||
defaultVal = val;
|
||||
}
|
||||
|
||||
// Set up rangeStr, if appropriate.
|
||||
VisParamDescr altVpd = vpd;
|
||||
if (vpd.CsType == typeof(int) || vpd.CsType == typeof(float)) {
|
||||
if (vpd.Special == VisParamDescr.SpecialMode.Offset) {
|
||||
defaultVal = mFormatter.FormatOffset24((int)defaultVal);
|
||||
rangeStr = "[" + mFormatter.FormatOffset24(0) + "," +
|
||||
mFormatter.FormatOffset24(mProject.FileDataLength - 1) + "]";
|
||||
|
||||
// Replace the vpd to provide a different min/max.
|
||||
altVpd = new VisParamDescr(vpd.UiLabel, vpd.Name, vpd.CsType,
|
||||
0, mProject.FileDataLength - 1, vpd.Special, vpd.DefaultValue);
|
||||
} else {
|
||||
rangeStr = "[" + vpd.Min + "," + vpd.Max + "]";
|
||||
}
|
||||
}
|
||||
|
||||
ParameterValue pv = new ParameterValue(vpd.UiLabel, vpd.Name, vpd.CsType,
|
||||
defaultVal, rangeStr);
|
||||
ParameterValue pv = new ParameterValue(altVpd, defaultVal, rangeStr);
|
||||
|
||||
mParameterList.Add(pv);
|
||||
ParameterList.Add(pv);
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,18 +180,140 @@ namespace SourceGen.WpfGui {
|
||||
}
|
||||
|
||||
private void OkButton_Click(object sender, RoutedEventArgs e) {
|
||||
Debug.WriteLine("PARAMS:");
|
||||
foreach (ParameterValue val in mParameterList) {
|
||||
Debug.WriteLine(" " + val.Name + ": " + val.Value +
|
||||
" (" + val.Value.GetType() + ")");
|
||||
// Generate value dictionary.
|
||||
Dictionary<string, object> valueDict =
|
||||
new Dictionary<string, object>(ParameterList.Count);
|
||||
foreach (ParameterValue pv in ParameterList) {
|
||||
if (pv.Descr.CsType == typeof(bool)) {
|
||||
Debug.Assert(pv.Value is bool);
|
||||
valueDict.Add(pv.Descr.Name, (bool)pv.Value);
|
||||
} else if (pv.Descr.CsType == typeof(int)) {
|
||||
int intVal;
|
||||
if (pv.Value is int) {
|
||||
intVal = (int)pv.Value;
|
||||
} else {
|
||||
bool ok = ParseInt((string)pv.Value, pv.Descr.Special, out intVal);
|
||||
Debug.Assert(ok);
|
||||
}
|
||||
valueDict.Add(pv.Descr.Name, intVal);
|
||||
} else if (pv.Descr.CsType == typeof(float)) {
|
||||
float floatVal;
|
||||
if (pv.Value is float || pv.Value is double) {
|
||||
floatVal = (float)pv.Value;
|
||||
} else {
|
||||
bool ok = float.TryParse((string)pv.Value, out floatVal);
|
||||
Debug.Assert(ok);
|
||||
}
|
||||
valueDict.Add(pv.Descr.Name, floatVal);
|
||||
} else {
|
||||
// skip it
|
||||
Debug.Assert(false);
|
||||
}
|
||||
}
|
||||
DialogResult = false; // TODO
|
||||
|
||||
VisDescr item = (VisDescr)visComboBox.SelectedItem;
|
||||
Debug.Assert(item != null);
|
||||
|
||||
NewVis = new Visualization(TagString, item.Ident, valueDict);
|
||||
DialogResult = true;
|
||||
}
|
||||
|
||||
private bool ParseInt(string str, VisParamDescr.SpecialMode special, out int intVal) {
|
||||
int numBase = (special == VisParamDescr.SpecialMode.Offset) ? 16 : 10;
|
||||
|
||||
string trimStr = str.Trim();
|
||||
if (trimStr.Length >= 1 && trimStr[0] == '+') {
|
||||
// May be present for an offset. Just ignore it. Don't use it as a radix char.
|
||||
trimStr = trimStr.Remove(0, 1);
|
||||
} else if (trimStr.Length >= 1 && trimStr[0] == '$') {
|
||||
numBase = 16;
|
||||
trimStr = trimStr.Remove(0, 1);
|
||||
} else if (trimStr.Length >= 2 && trimStr[0] == '0' &&
|
||||
(trimStr[1] == 'x' || trimStr[1] == 'X')) {
|
||||
numBase = 16;
|
||||
trimStr = trimStr.Remove(0, 2);
|
||||
}
|
||||
if (trimStr.Length == 0) {
|
||||
intVal = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
intVal = Convert.ToInt32(trimStr, numBase);
|
||||
return true;
|
||||
} catch (Exception) {
|
||||
intVal = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckValid() {
|
||||
IsValid = true;
|
||||
|
||||
string trimTag = TagString.Trim();
|
||||
if (trimTag.Length < MIN_TRIMMED_TAG_LEN) {
|
||||
IsValid = false;
|
||||
TagLabelBrush = mErrorLabelColor;
|
||||
} else {
|
||||
TagLabelBrush = mDefaultLabelColor;
|
||||
}
|
||||
// TODO: verify tag is unique
|
||||
|
||||
foreach (ParameterValue pv in ParameterList) {
|
||||
pv.ForegroundBrush = mDefaultLabelColor;
|
||||
if (pv.Descr.CsType == typeof(bool)) {
|
||||
// always fine
|
||||
continue;
|
||||
} else if (pv.Descr.CsType == typeof(int)) {
|
||||
// integer, possibly Offset special
|
||||
bool ok = true;
|
||||
int intVal;
|
||||
if (pv.Value is int) {
|
||||
// happens initially, before the TextBox can futz with it
|
||||
intVal = (int)pv.Value;
|
||||
} else if (!ParseInt((string)pv.Value, pv.Descr.Special, out intVal)) {
|
||||
ok = false;
|
||||
}
|
||||
if (ok && (intVal < (int)pv.Descr.Min || intVal > (int)pv.Descr.Max)) {
|
||||
// TODO(someday): make the range text red instead of the label
|
||||
ok = false;
|
||||
}
|
||||
if (!ok) {
|
||||
pv.ForegroundBrush = mErrorLabelColor;
|
||||
IsValid = false;
|
||||
}
|
||||
} else if (pv.Descr.CsType == typeof(float)) {
|
||||
// float
|
||||
} else {
|
||||
// unexpected
|
||||
Debug.Assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void VisComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
VisDescr item = (VisDescr)visComboBox.SelectedItem;
|
||||
if (item == null) {
|
||||
Debug.Assert(false); // not expected
|
||||
return;
|
||||
}
|
||||
Debug.WriteLine("VisComboBox sel change: " + item.Ident);
|
||||
GenerateParamControls(item);
|
||||
CheckValid();
|
||||
}
|
||||
|
||||
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
|
||||
TextBox src = (TextBox)sender;
|
||||
ParameterValue pv = (ParameterValue)src.DataContext;
|
||||
Debug.WriteLine("TEXT CHANGE " + pv + ": " + src.Text);
|
||||
//Debug.WriteLine("TEXT CHANGE " + pv + ": " + src.Text);
|
||||
CheckValid();
|
||||
}
|
||||
|
||||
private void CheckBox_Changed(object sender, RoutedEventArgs e) {
|
||||
CheckBox src = (CheckBox)sender;
|
||||
ParameterValue pv = (ParameterValue)src.DataContext;
|
||||
//Debug.WriteLine("CHECK CHANGE" + pv);
|
||||
CheckValid();
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,26 +321,44 @@ namespace SourceGen.WpfGui {
|
||||
/// Describes a parameter and holds its value while being edited by WPF.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We use an explicit type so that we can format the initial value as hex or whatever.
|
||||
/// We currently detect updates with change events. We could also tweak the Value setter
|
||||
/// to fire an event back to the window class when things change. I don't know that there's
|
||||
/// an advantage to doing so.
|
||||
/// </remarks>
|
||||
public class ParameterValue {
|
||||
public string UiName { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public Type CsType { get; private set; }
|
||||
public object Value { get; set; }
|
||||
public class ParameterValue : INotifyPropertyChanged {
|
||||
public VisParamDescr Descr { get; private set; }
|
||||
public string UiString { get; private set; }
|
||||
public string RangeText { get; private set; }
|
||||
|
||||
public ParameterValue(string uiName, string name, Type csType, object val,
|
||||
string rangeText) {
|
||||
UiName = uiName;
|
||||
Name = name;
|
||||
CsType = csType;
|
||||
private object mValue;
|
||||
public object Value {
|
||||
get { return mValue; }
|
||||
set { mValue = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
private Brush mForegroundBrush;
|
||||
public Brush ForegroundBrush {
|
||||
get { return mForegroundBrush; }
|
||||
set { mForegroundBrush = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
// INotifyPropertyChanged implementation
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
public ParameterValue(VisParamDescr vpd, object val, string rangeText) {
|
||||
Descr = vpd;
|
||||
Value = val;
|
||||
RangeText = rangeText;
|
||||
|
||||
char labelSuffix = (vpd.CsType == typeof(bool)) ? '?' : ':';
|
||||
UiString = vpd.UiLabel + labelSuffix;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "[PV: " + Name + "=" + Value + "]";
|
||||
return "[PV: " + Descr.Name + "=" + Value + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,11 +382,11 @@ namespace SourceGen.WpfGui {
|
||||
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
|
||||
if (item is ParameterValue) {
|
||||
ParameterValue parm = (ParameterValue)item;
|
||||
if (parm.CsType == typeof(bool)) {
|
||||
if (parm.Descr.CsType == typeof(bool)) {
|
||||
return BoolTemplate;
|
||||
} else if (parm.CsType == typeof(int)) {
|
||||
} else if (parm.Descr.CsType == typeof(int)) {
|
||||
return IntTemplate;
|
||||
} else if (parm.CsType == typeof(float)) {
|
||||
} else if (parm.Descr.CsType == typeof(float)) {
|
||||
return FloatTemplate;
|
||||
} else {
|
||||
Debug.WriteLine("WHA?" + parm.Value.GetType());
|
||||
|
@ -91,6 +91,9 @@ namespace SourceGen.WpfGui {
|
||||
private void NewButton_Click(object sender, RoutedEventArgs e) {
|
||||
VisualizationList.Add(new Visualization("VIS #" + VisualizationList.Count,
|
||||
"apple2-hi-res-bitmap", new Dictionary<string, object>()));
|
||||
|
||||
// TODO: disable New button if no appropriate vis plugins can be found (or maybe
|
||||
// MessageBox here)
|
||||
}
|
||||
|
||||
private void EditButton_Click(object sender, RoutedEventArgs e) {
|
||||
@ -100,8 +103,11 @@ namespace SourceGen.WpfGui {
|
||||
EditVisualization dlg = new EditVisualization(this, mProject, mFormatter,
|
||||
new Visualization("arbitrary tag", "apple2-hi-res-bitmap", testDict));
|
||||
if (dlg.ShowDialog() == true) {
|
||||
// TODO
|
||||
Visualization newVis = dlg.NewVis;
|
||||
Debug.WriteLine("New vis: " + newVis);
|
||||
}
|
||||
|
||||
// TODO: disable edit button if matching vis can't be found (or maybe MessageBox)
|
||||
}
|
||||
|
||||
private void RemoveButton_Click(object sender, RoutedEventArgs e) {
|
||||
|
Loading…
Reference in New Issue
Block a user