1
0
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:
Andy McFadden 2019-11-26 18:54:42 -08:00
parent 836626bdc3
commit e7fccfda03
12 changed files with 510 additions and 136 deletions

View File

@ -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
}
}
}

View File

@ -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>

View File

@ -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.

View File

@ -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();
}
}

View File

@ -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");
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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; }

View File

@ -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 {

View File

@ -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>

View File

@ -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());

View File

@ -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) {