mirror of
https://github.com/fadden/6502bench.git
synced 2025-02-08 05:30:35 +00:00
Add Atari 2600 sprite/playfield visualizer
First swing at a visualizer for Atari 2600 sprites and playfields. Won't necessarily present an accurate view of what is displayed on screen, but should provide a reasonable shape for data stored in the obvious way. The Adventure playfields looked squashed, so I added a simple row duplication value. Also, minor improvements to visualizers generally: - Throw an exception, rather than an Assert, in VisBitmap8 when the arguments are bad. - Show the exception in the Visualization Edit dialog. - If generation fails and we don't have an error message, show a generic "stuff be broke" string. - Set focus on OK button in Visualization Set Edit after editing, so you can hit Enter twice after renaming a tag.
This commit is contained in:
parent
1cdb31de32
commit
5635a1e33a
@ -43,8 +43,9 @@ namespace PluginCommon {
|
||||
/// <param name="width">Bitmap width, in pixels.</param>
|
||||
/// <param name="height">Bitmap height, in pixels.</param>
|
||||
public VisBitmap8(int width, int height) {
|
||||
Debug.Assert(width > 0 && width <= MAX_DIMENSION);
|
||||
Debug.Assert(height > 0 && height <= MAX_DIMENSION);
|
||||
if (width <= 0 || width > MAX_DIMENSION || height <= 0 || height > MAX_DIMENSION) {
|
||||
throw new ArgumentException("Bad bitmap width/height " + width + "," + height);
|
||||
}
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
|
@ -175,6 +175,6 @@ limitations under the License.
|
||||
<system:String x:Key="str_TitleNewProject">[new project]</system:String>
|
||||
<system:String x:Key="str_TitleReadOnly">*READ-ONLY*</system:String>
|
||||
<system:String x:Key="str_Unset">[unset]</system:String>
|
||||
<system:String x:Key="str_VisSetMultipleFmt">{0}: {1} (+{2} more)</system:String>
|
||||
<system:String x:Key="str_VisSetSingleFmt">{0}: {1}</system:String>
|
||||
<system:String x:Key="str_VisSetMultipleFmt">{1} (+{2} more)</system:String>
|
||||
<system:String x:Key="str_VisSetSingleFmt">{1}</system:String>
|
||||
</ResourceDictionary>
|
230
SourceGen/RuntimeData/Atari/VisAtari2600.cs
Normal file
230
SourceGen/RuntimeData/Atari/VisAtari2600.cs
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
|
||||
using PluginCommon;
|
||||
|
||||
namespace RuntimeData.Atari {
|
||||
public class VisAtari2600 : MarshalByRefObject, IPlugin, IPlugin_Visualizer {
|
||||
// IPlugin
|
||||
public string Identifier {
|
||||
get { return "Atari 2600 Graphic Visualizer"; }
|
||||
}
|
||||
private IApplication mAppRef;
|
||||
private byte[] mFileData;
|
||||
private AddressTranslate mAddrTrans;
|
||||
|
||||
// Visualization identifiers; DO NOT change or projects that use them will break.
|
||||
private const string VIS_GEN_SPRITE = "atari2600-sprite";
|
||||
private const string VIS_GEN_PLAYFIELD = "atari2600-playfield";
|
||||
|
||||
private const string P_OFFSET = "offset";
|
||||
private const string P_HEIGHT = "height";
|
||||
private const string P_ROW_DUP = "rowDup";
|
||||
private const string P_REFLECTED = "reflected";
|
||||
|
||||
private const int MAX_HEIGHT = 192;
|
||||
private const int HALF_WIDTH = 20;
|
||||
|
||||
// Visualization descriptors.
|
||||
private VisDescr[] mDescriptors = new VisDescr[] {
|
||||
new VisDescr(VIS_GEN_SPRITE, "Atari 2600 Sprite", VisDescr.VisType.Bitmap,
|
||||
new VisParamDescr[] {
|
||||
new VisParamDescr("File offset (hex)",
|
||||
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0),
|
||||
new VisParamDescr("Height",
|
||||
P_HEIGHT, typeof(int), 1, 192, 0, 1),
|
||||
}),
|
||||
new VisDescr(VIS_GEN_PLAYFIELD, "Atari 2600 Playfield", VisDescr.VisType.Bitmap,
|
||||
new VisParamDescr[] {
|
||||
new VisParamDescr("File offset (hex)",
|
||||
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0),
|
||||
new VisParamDescr("Height",
|
||||
P_HEIGHT, typeof(int), 1, 192, 0, 1),
|
||||
new VisParamDescr("Row duplication",
|
||||
P_ROW_DUP, typeof(int), 0, 10, 0, 3),
|
||||
new VisParamDescr("Reflected",
|
||||
P_REFLECTED, typeof(bool), 0, 0, 0, false),
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
// IPlugin
|
||||
public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) {
|
||||
mAppRef = appRef;
|
||||
mFileData = fileData;
|
||||
mAddrTrans = addrTrans;
|
||||
}
|
||||
|
||||
// IPlugin
|
||||
public void Unprepare() {
|
||||
mAppRef = null;
|
||||
mFileData = null;
|
||||
mAddrTrans = null;
|
||||
}
|
||||
|
||||
// IPlugin_Visualizer
|
||||
public VisDescr[] GetVisGenDescrs() {
|
||||
// We're using a static set, but it could be generated based on file contents.
|
||||
// Confirm that we're prepared.
|
||||
if (mFileData == null) {
|
||||
return null;
|
||||
}
|
||||
return mDescriptors;
|
||||
}
|
||||
|
||||
// IPlugin_Visualizer
|
||||
public IVisualization2d Generate2d(VisDescr descr,
|
||||
ReadOnlyDictionary<string, object> parms) {
|
||||
switch (descr.Ident) {
|
||||
case VIS_GEN_SPRITE:
|
||||
return GenerateSprite(parms);
|
||||
case VIS_GEN_PLAYFIELD:
|
||||
return GeneratePlayfield(parms);
|
||||
default:
|
||||
mAppRef.ReportError("Unknown ident " + descr.Ident);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IVisualization2d GenerateSprite(ReadOnlyDictionary<string, object> parms) {
|
||||
int offset = Util.GetFromObjDict(parms, P_OFFSET, 0);
|
||||
int height = Util.GetFromObjDict(parms, P_HEIGHT, 1);
|
||||
|
||||
if (offset < 0 || offset >= mFileData.Length ||
|
||||
height <= 0 || height > MAX_HEIGHT) {
|
||||
// the UI should flag these based on range (and ideally wouldn't have called us)
|
||||
mAppRef.ReportError("Invalid parameter");
|
||||
return null;
|
||||
}
|
||||
|
||||
int lastOffset = offset + height - 1;
|
||||
if (lastOffset >= mFileData.Length) {
|
||||
mAppRef.ReportError("Sprite runs off end of file (last offset +" +
|
||||
lastOffset.ToString("x6") + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
VisBitmap8 vb = new VisBitmap8(8, height);
|
||||
SetPalette(vb);
|
||||
|
||||
for (int row = 0; row < height; row++) {
|
||||
byte val = mFileData[offset + row];
|
||||
for (int col = 0; col < 8; col++) {
|
||||
if ((val & 0x80) != 0) {
|
||||
vb.SetPixelIndex(col, row, (byte)Color.White);
|
||||
} else {
|
||||
vb.SetPixelIndex(col, row, (byte)Color.Black);
|
||||
}
|
||||
val <<= 1;
|
||||
}
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
|
||||
private IVisualization2d GeneratePlayfield(ReadOnlyDictionary<string, object> parms) {
|
||||
const int BYTE_WIDTH = 3;
|
||||
|
||||
int offset = Util.GetFromObjDict(parms, P_OFFSET, 0);
|
||||
int height = Util.GetFromObjDict(parms, P_HEIGHT, 1);
|
||||
int rowDup = Util.GetFromObjDict(parms, P_ROW_DUP, 3);
|
||||
bool isReflected = Util.GetFromObjDict(parms, P_REFLECTED, false);
|
||||
|
||||
if (offset < 0 || offset >= mFileData.Length ||
|
||||
height <= 0 || height > MAX_HEIGHT) {
|
||||
// the UI should flag these based on range (and ideally wouldn't have called us)
|
||||
mAppRef.ReportError("Invalid parameter");
|
||||
return null;
|
||||
}
|
||||
|
||||
int lastOffset = offset + BYTE_WIDTH * height - 1;
|
||||
if (lastOffset >= mFileData.Length) {
|
||||
mAppRef.ReportError("Playfield runs off end of file (last offset +" +
|
||||
lastOffset.ToString("x6") + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
int rowHeight = rowDup + 1;
|
||||
|
||||
// Each half of the playfield is 20 bits wide.
|
||||
VisBitmap8 vb = new VisBitmap8(40, height * rowHeight);
|
||||
SetPalette(vb);
|
||||
|
||||
for (int row = 0; row < height; row++) {
|
||||
// Assume data is stored as PF0,PF1,PF2. PF0/PF2 are in reverse order, so
|
||||
// start by assembling them as a reversed 20-bit word.
|
||||
int srcOff = offset + row * BYTE_WIDTH;
|
||||
int rev = (mFileData[srcOff] >> 4) | (RevBits(mFileData[srcOff + 1], 8) << 4) |
|
||||
(mFileData[srcOff + 2] << 12);
|
||||
|
||||
// Now generate the forward order.
|
||||
int fwd = RevBits(rev, 20);
|
||||
|
||||
// Render the first part of the line forward.
|
||||
RenderHalfField(vb, row * rowHeight, rowHeight, 0,
|
||||
fwd, Color.White);
|
||||
// Render the second half forward or reversed, in grey.
|
||||
RenderHalfField(vb, row * rowHeight, rowHeight, HALF_WIDTH,
|
||||
isReflected ? rev : fwd, Color.Grey);
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
|
||||
private int RevBits(int val, int count) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
result <<= 1;
|
||||
result |= val & 0x01;
|
||||
val >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void RenderHalfField(VisBitmap8 vb, int row, int rowDup, int startCol, int val,
|
||||
Color setColor) {
|
||||
for (int col = startCol; col < startCol + HALF_WIDTH; col++) {
|
||||
val <<= 1;
|
||||
byte colorIdx;
|
||||
if ((val & (1 << HALF_WIDTH)) != 0) {
|
||||
colorIdx = (byte)setColor;
|
||||
} else {
|
||||
colorIdx = (byte)Color.Black;
|
||||
}
|
||||
|
||||
for (int r = row; r < row + rowDup; r++) {
|
||||
vb.SetPixelIndex(col, r, colorIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum Color : byte {
|
||||
Transparent = 0,
|
||||
Black = 1,
|
||||
White = 2,
|
||||
Grey = 3
|
||||
}
|
||||
|
||||
private void SetPalette(VisBitmap8 vb) {
|
||||
vb.AddColor(0, 0, 0, 0); // 0=transparent
|
||||
vb.AddColor(0xff, 0x00, 0x00, 0x00); // 1=black
|
||||
vb.AddColor(0xff, 0xff, 0xff, 0xff); // 2=white
|
||||
vb.AddColor(0xff, 0xd0, 0xd0, 0xd0); // 3=grey
|
||||
}
|
||||
}
|
||||
}
|
@ -108,7 +108,7 @@ Some less-common parameters include:</p>
|
||||
visualizer will default to no interleave (stride == 1).</li>
|
||||
</ul>
|
||||
|
||||
<h3>Apple II - VisHiRes</h3>
|
||||
<h3>Apple II - Apple/VisHiRes</h3>
|
||||
|
||||
<p>There is no standard format for small hi-res bitmaps, but certain
|
||||
arrangements are common. The script defines three generators:</p>
|
||||
@ -139,6 +139,23 @@ but has no effect on black or white.</p>
|
||||
<p>The converter generates one output pixel for every source pixel, so
|
||||
half-pixel shifts are not rendered.</p>
|
||||
|
||||
<h3>Atari 2600 - Atari/VisAtari2600</h3>
|
||||
|
||||
<p>The Atari 2600 graphics system has registers that determine the
|
||||
appearance of a sprite or playfield on a single row. The visualization
|
||||
generator works for data stored in a straightforward fashion.</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Sprite</b> - basic 1xN sprite, converted to an image 8 pixels
|
||||
wide.</li>
|
||||
<li><b>Playfield</b> - assumes PF0,PF1,PF2 are stored in that order,
|
||||
multiple entries following each other. Specify the number of
|
||||
3-byte entries as the height.
|
||||
Since most playfields aren't the full height of the screen,
|
||||
it will tend to look squashed. Use the "row duplication" feature
|
||||
to repeat each row N times to make it look more like it should.</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
|
@ -137,6 +137,7 @@
|
||||
"RT:Atari/2600.sym65"
|
||||
],
|
||||
"ExtensionScripts" : [
|
||||
"RT:Atari/VisAtari2600.cs"
|
||||
],
|
||||
"Parameters" : {
|
||||
"load-address":"0xf000"
|
||||
|
BIN
SourceGen/SGTestData/Visualization/atari2600-sprite-test
Normal file
BIN
SourceGen/SGTestData/Visualization/atari2600-sprite-test
Normal file
Binary file not shown.
103
SourceGen/SGTestData/Visualization/atari2600-sprite-test.S
Normal file
103
SourceGen/SGTestData/Visualization/atari2600-sprite-test.S
Normal file
@ -0,0 +1,103 @@
|
||||
; Copyright 2019 faddenSoft. All Rights Reserved.
|
||||
; See the LICENSE.txt file for distribution terms (Apache 2.0).
|
||||
;
|
||||
; Assembler: Merlin 32
|
||||
|
||||
org $f000
|
||||
|
||||
bit sprite1
|
||||
bit sprite2
|
||||
bit sprite3
|
||||
nop
|
||||
bit playfield1
|
||||
bit playfield2
|
||||
bit playfield3
|
||||
rts
|
||||
|
||||
; Atari Adventure: GfxKey (1x3)
|
||||
sprite1
|
||||
dfb $07
|
||||
dfb $fd
|
||||
dfb $a7
|
||||
dfb $00
|
||||
|
||||
; Atari Adventure: GfxDrag0 (1x20)
|
||||
sprite2
|
||||
dfb $06
|
||||
dfb $0f
|
||||
dfb $f3
|
||||
dfb $fe
|
||||
dfb $0e
|
||||
dfb $04
|
||||
dfb $04
|
||||
dfb $1e
|
||||
dfb $3f
|
||||
dfb $7f
|
||||
dfb $e3
|
||||
dfb $c3
|
||||
dfb $c3
|
||||
dfb $c7
|
||||
dfb $ff
|
||||
dfb $3c
|
||||
dfb $08
|
||||
dfb $8f
|
||||
dfb $e1
|
||||
dfb $3f
|
||||
dfb $00
|
||||
|
||||
; Atari Adventure: GfxDrag1 (1x22)
|
||||
sprite3
|
||||
dfb $80
|
||||
dfb $40
|
||||
dfb $26
|
||||
dfb $1f
|
||||
dfb $0b
|
||||
dfb $0e
|
||||
dfb $1e
|
||||
dfb $24
|
||||
dfb $44
|
||||
dfb $8e
|
||||
dfb $1e
|
||||
dfb $3f
|
||||
dfb $7f
|
||||
dfb $7f
|
||||
dfb $7f
|
||||
dfb $7f
|
||||
dfb $3e
|
||||
dfb $1c
|
||||
dfb $08
|
||||
dfb $f8
|
||||
dfb $80
|
||||
dfb $e0
|
||||
dfb $00
|
||||
|
||||
|
||||
; Atari Adventure: BlackMaze3 -- x7 not reflected
|
||||
playfield1
|
||||
dfb $f0,$f0,$ff
|
||||
dfb $30,$00,$00
|
||||
dfb $30,$3f,$ff
|
||||
dfb $00,$30,$00
|
||||
dfb $f0,$f0,$ff
|
||||
dfb $30,$00,$03
|
||||
dfb $f0,$f0,$ff
|
||||
|
||||
; Atari Adventure: RedMazeBottom -- x7 reflected
|
||||
playfield2
|
||||
dfb $f0,$33,$cf
|
||||
dfb $f0,$30,$00
|
||||
dfb $f0,$33,$ff
|
||||
dfb $00,$33,$00
|
||||
dfb $f0,$ff,$00
|
||||
dfb $00,$00,$00
|
||||
dfb $f0,$ff,$0f
|
||||
|
||||
; Atari Adventure: CastleDef -- x7 reflected
|
||||
playfield3
|
||||
dfb $f0,$fe,$15
|
||||
dfb $30,$03,$1f
|
||||
dfb $30,$03,$ff
|
||||
dfb $30,$00,$ff
|
||||
dfb $30,$00,$3f
|
||||
dfb $30,$00,$00
|
||||
dfb $f0,$ff,$0f
|
@ -0,0 +1,71 @@
|
||||
### 6502bench SourceGen dis65 v1.0 ###
|
||||
{
|
||||
"_ContentVersion":3,"FileDataLength":131,"FileDataCrc32":-1864286961,"ProjectProps":{
|
||||
"CpuName":"6502","IncludeUndocumentedInstr":false,"TwoByteBrk":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{
|
||||
"AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":true,"SmartPlpHandling":true},
|
||||
"PlatformSymbolFileIdentifiers":["RT:Atari/2600.sym65"],"ExtensionScriptFileIdentifiers":["RT:Atari/VisAtari2600.cs"],"ProjectSyms":{
|
||||
}},
|
||||
"AddressMap":[{
|
||||
"Offset":0,"Addr":61440}],"TypeHints":[{
|
||||
"Low":0,"High":0,"Hint":"Code"}],"StatusFlagOverrides":{
|
||||
},
|
||||
"Comments":{
|
||||
},
|
||||
"LongComments":{
|
||||
"-2147483647":{
|
||||
"Text":"A few sprites and playfield graphics from Atari Adventure.\r\n","BoxMode":false,"MaxWidth":80,"BackgroundColor":0}},
|
||||
"Notes":{
|
||||
},
|
||||
"UserLabels":{
|
||||
"110":{
|
||||
"Label":"CastleDef","Value":61550,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||
"89":{
|
||||
"Label":"RedMazeBottom","Value":61529,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||
"68":{
|
||||
"Label":"BlackMaze3","Value":61508,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||
"45":{
|
||||
"Label":"GfxDrag1","Value":61485,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||
"24":{
|
||||
"Label":"GfxDrag0","Value":61464,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||
"20":{
|
||||
"Label":"GfxKey","Value":61460,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"}},
|
||||
"OperandFormats":{
|
||||
"20":{
|
||||
"Length":4,"Format":"Dense","SubFormat":"None","SymbolRef":null},
|
||||
"24":{
|
||||
"Length":21,"Format":"Dense","SubFormat":"None","SymbolRef":null},
|
||||
"45":{
|
||||
"Length":23,"Format":"Dense","SubFormat":"None","SymbolRef":null},
|
||||
"68":{
|
||||
"Length":21,"Format":"Dense","SubFormat":"None","SymbolRef":null},
|
||||
"89":{
|
||||
"Length":21,"Format":"Dense","SubFormat":"None","SymbolRef":null},
|
||||
"110":{
|
||||
"Length":21,"Format":"Dense","SubFormat":"None","SymbolRef":null}},
|
||||
"LvTables":{
|
||||
},
|
||||
"VisualizationSets":{
|
||||
"20":{
|
||||
"Items":[{
|
||||
"Tag":"Key sprite","VisGenIdent":"atari2600-sprite","VisGenParams":{
|
||||
"offset":20,"height":3}}]},
|
||||
"24":{
|
||||
"Items":[{
|
||||
"Tag":"Dragon 0","VisGenIdent":"atari2600-sprite","VisGenParams":{
|
||||
"offset":24,"height":20}}]},
|
||||
"45":{
|
||||
"Items":[{
|
||||
"Tag":"Dragon 1","VisGenIdent":"atari2600-sprite","VisGenParams":{
|
||||
"offset":45,"height":22}}]},
|
||||
"68":{
|
||||
"Items":[{
|
||||
"Tag":"Black Maze 3","VisGenIdent":"atari2600-playfield","VisGenParams":{
|
||||
"offset":68,"height":7,"rowDup":3,"reflected":false}}]},
|
||||
"89":{
|
||||
"Items":[{
|
||||
"Tag":"Red Maze (bottom)","VisGenIdent":"atari2600-playfield","VisGenParams":{
|
||||
"offset":89,"height":7,"rowDup":3,"reflected":true}}]},
|
||||
"110":{
|
||||
"Items":[{
|
||||
"Tag":"Castle Def","VisGenIdent":"atari2600-playfield","VisGenParams":{
|
||||
"offset":110,"height":7,"rowDup":3,"reflected":true}}]}}}
|
@ -29,6 +29,8 @@ limitations under the License.
|
||||
Closed="Window_Closed">
|
||||
|
||||
<Window.Resources>
|
||||
<system:String x:Key="str_VisGenFailed">Visualization generation failed</system:String>
|
||||
|
||||
<!-- big thanks: http://drwpf.com/blog/2008/01/03/itemscontrol-d-is-for-datatemplate/ -->
|
||||
<DataTemplate x:Key="BoolTemplate">
|
||||
<Grid>
|
||||
|
@ -433,12 +433,17 @@ namespace SourceGen.WpfGui {
|
||||
} catch (Exception ex) {
|
||||
Debug.WriteLine("Vis generation failed: " + ex);
|
||||
vis2d = null;
|
||||
if (string.IsNullOrEmpty(LastPluginMessage)) {
|
||||
LastPluginMessage = ex.Message;
|
||||
}
|
||||
}
|
||||
if (vis2d == null) {
|
||||
previewImage.Source = sBadParamsImage;
|
||||
if (!string.IsNullOrEmpty(LastPluginMessage)) {
|
||||
// Report the last message we got as an error.
|
||||
PluginErrMessage = LastPluginMessage;
|
||||
} else {
|
||||
PluginErrMessage = (string)FindResource("str_VisGenFailed");
|
||||
}
|
||||
IsValid = false;
|
||||
} else {
|
||||
|
@ -103,7 +103,7 @@ limitations under the License.
|
||||
|
||||
<DockPanel Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" 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"
|
||||
<Button DockPanel.Dock="Right" Name="okButton" Grid.Column="1" Content="OK" Width="70"
|
||||
IsDefault="True" Click="OkButton_Click"/>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
|
@ -195,6 +195,8 @@ namespace SourceGen.WpfGui {
|
||||
VisualizationList.Remove(item);
|
||||
VisualizationList.Insert(index, dlg.NewVis);
|
||||
visualizationGrid.SelectedIndex = index;
|
||||
|
||||
okButton.Focus();
|
||||
}
|
||||
|
||||
private void RemoveButton_Click(object sender, RoutedEventArgs e) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user