1
0
mirror of https://github.com/fadden/6502bench.git synced 2025-02-20 06:29:04 +00:00

Rework column width input

Took another swing at doing things WPF-style.  Ended up with TextBox
objects backed by integer fields, which worked pretty well but
didn't get the min/max values.  Set up validation instead, once I
figured out how to make it update in real time.
This commit is contained in:
Andy McFadden 2019-06-28 15:17:48 -07:00
parent 5beae8f926
commit 7e0faf2800
2 changed files with 169 additions and 70 deletions

View File

@ -165,17 +165,45 @@ limitations under the License.
HorizontalAlignment="Right" Margin="0,0,4,0"/> HorizontalAlignment="Right" Margin="0,0,4,0"/>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal"> <StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal">
<TextBox Name="asmLabelColWidthTextBox" Width="60" Margin="0,0,4,0" <TextBox Name="asmLabelColWidthTextBox" Width="60" Margin="0,0,4,0"
PreviewTextInput="CheckWidthInput" TextChanged="AsmColWidthTextBox_TextChanged">
TextChanged="AsmLabelColWidthTextBox_TextChanged"/> <TextBox.Text>
<Binding Path="AsmLabelColWidth" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:AsmColWidthRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox Name="asmOpcodeColWidthTextBox" Width="60" Margin="0,0,4,0" <TextBox Name="asmOpcodeColWidthTextBox" Width="60" Margin="0,0,4,0"
PreviewTextInput="CheckWidthInput" TextChanged="AsmColWidthTextBox_TextChanged">
TextChanged="AsmLabelColWidthTextBox_TextChanged"/> <TextBox.Text>
<Binding Path="AsmOpcodeColWidth" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:AsmColWidthRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox Name="asmOperandColWidthTextBox" Width="60" Margin="0,0,4,0" <TextBox Name="asmOperandColWidthTextBox" Width="60" Margin="0,0,4,0"
PreviewTextInput="CheckWidthInput" TextChanged="AsmColWidthTextBox_TextChanged">
TextChanged="AsmLabelColWidthTextBox_TextChanged"/> <TextBox.Text>
<Binding Path="AsmOperandColWidth" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:AsmColWidthRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox Name="asmCommentColWidthTextBox" Width="60" Margin="0,0,8,0" <TextBox Name="asmCommentColWidthTextBox" Width="60" Margin="0,0,8,0"
PreviewTextInput="CheckWidthInput" TextChanged="AsmColWidthTextBox_TextChanged">
TextChanged="AsmLabelColWidthTextBox_TextChanged"/> <TextBox.Text>
<Binding Path="AsmCommentColWidth" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:AsmColWidthRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock Text="(label, opcode, operand, comment)"/> <TextBlock Text="(label, opcode, operand, comment)"/>
</StackPanel> </StackPanel>
</Grid> </Grid>

View File

@ -17,11 +17,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.Win32; using Microsoft.Win32;
using CommonUtil; using CommonUtil;
@ -448,44 +448,113 @@ namespace SourceGenWPF.WpfGui {
#region Asm Config #region Asm Config
public const int ASM_COL_MIN_WIDTH = 1;
public const int ASM_COL_MAX_WIDTH = 200;
//
// Numeric input fields, bound directly to TextBox.Text. This performs the basic
// validation, but we also do our own so we can cap min/max. The custom validation
// rule seems to fire ahead of the field assignment, so if our rule fails we won't
// try to assign here at all.
//
// The validation approach seems to make the most sense here because we don't dim the
// Apply/OK buttons when invalid input is present. Instead we just set the width to
// the minimum value when validation fails.
//
// See also https://stackoverflow.com/a/44586784/294248
//
private int mAsmLabelColWidth;
public int AsmLabelColWidth {
get { return mAsmLabelColWidth; }
set {
if (mAsmLabelColWidth != value) {
mAsmLabelColWidth = value;
OnPropertyChanged();
IsDirty = true;
}
}
}
private int mAsmOpcodeColWidth;
public int AsmOpcodeColWidth {
get { return mAsmOpcodeColWidth; }
set {
if (mAsmOpcodeColWidth != value) {
mAsmOpcodeColWidth = value;
OnPropertyChanged();
IsDirty = true;
}
}
}
private int mAsmOperandColWidth;
public int AsmOperandColWidth {
get { return mAsmOperandColWidth; }
set {
if (mAsmOperandColWidth != value) {
mAsmOperandColWidth = value;
OnPropertyChanged();
IsDirty = true;
}
}
}
private int mAsmCommentColWidth;
public int AsmCommentColWidth {
get { return mAsmCommentColWidth; }
set {
if (mAsmCommentColWidth != value) {
mAsmCommentColWidth = value;
OnPropertyChanged();
IsDirty = true;
}
}
}
// checkboxes
private bool mShowCycleCounts; private bool mShowCycleCounts;
public bool ShowCycleCounts { public bool ShowCycleCounts {
get { return mShowCycleCounts; } get { return mShowCycleCounts; }
set { set {
mShowCycleCounts = value; if (mShowCycleCounts != value) {
OnPropertyChanged(); mShowCycleCounts = value;
mSettings.SetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, value); OnPropertyChanged();
IsDirty = true; mSettings.SetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, value);
IsDirty = true;
}
} }
} }
private bool mLongLabelNewLine; private bool mLongLabelNewLine;
public bool LongLabelNewLine { public bool LongLabelNewLine {
get { return mLongLabelNewLine; } get { return mLongLabelNewLine; }
set { set {
mLongLabelNewLine = value; if (mLongLabelNewLine != value) {
OnPropertyChanged(); mLongLabelNewLine = value;
mSettings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, value); OnPropertyChanged();
IsDirty = true; mSettings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, value);
IsDirty = true;
}
} }
} }
private bool mAddIdentComment; private bool mAddIdentComment;
public bool AddIdentComment { public bool AddIdentComment {
get { return mAddIdentComment; } get { return mAddIdentComment; }
set { set {
mAddIdentComment = value; if (mAddIdentComment != value) {
OnPropertyChanged(); mAddIdentComment = value;
mSettings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, value); OnPropertyChanged();
IsDirty = true; mSettings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, value);
IsDirty = true;
}
} }
} }
private bool mDisableLabelLocalization; private bool mDisableLabelLocalization;
public bool DisableLabelLocalization { public bool DisableLabelLocalization {
get { return mDisableLabelLocalization; } get { return mDisableLabelLocalization; }
set { set {
mDisableLabelLocalization = value; if (mDisableLabelLocalization != value) {
OnPropertyChanged(); mDisableLabelLocalization = value;
mSettings.SetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, value); OnPropertyChanged();
IsDirty = true; mSettings.SetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, value);
IsDirty = true;
}
} }
} }
@ -535,36 +604,19 @@ namespace SourceGenWPF.WpfGui {
} }
/// <summary> /// <summary>
/// Checks whether the character typed into a column width entry field is allowed. /// Updates the assembler config settings whenever one of the width text fields is edited.
/// This is only useful for screening the character set, not the field contents.
/// </summary>
/// <remarks>
/// This just screens the character. Doesn't handle selection operations like pasting.
/// Also, doesn't fire when you hit the space bar. This is only slightly better than
/// useless, but since we don't otherwise give an indication of wrongness it's nice
/// to have.
///
/// Another approach is to bind Text to an integer property. This enables the validation
/// mechanism, which puts a red box around the field when it contains bad things, but
/// only after focus leaves the field.
///
/// See also https://stackoverflow.com/q/1268552/294248
/// </remarks>
private void CheckWidthInput(object sender, TextCompositionEventArgs e) {
// Set e.Handled to true if the character is invalid.
char ch = e.Text[0];
e.Handled = (ch < '0' || ch > '9');
}
/// <summary>
/// Updates the assembler config settings whenever one of the text fields is edited.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This fires 4x every time the combo box selection changes, as the new fields are /// This fires 4x every time the combo box selection changes, as the new fields are
/// populated. That should work out correctly. /// populated. That means we'll have incorrect intermediate states, but should
/// finish up correctly.
/// </remarks> /// </remarks>
private void AsmLabelColWidthTextBox_TextChanged(object sender, TextChangedEventArgs e) { private void AsmColWidthTextBox_TextChanged(object sender, TextChangedEventArgs e) {
AssemblerInfo asm = (AssemblerInfo)asmConfigComboBox.SelectedItem; AssemblerInfo asm = (AssemblerInfo)asmConfigComboBox.SelectedItem;
if (asm == null) {
// fires during dialog initialization, before anything is selected
return;
}
AssemblerConfig.SetConfig(mSettings, asm.AssemblerId, GetAsmConfigFromUi()); AssemblerConfig.SetConfig(mSettings, asm.AssemblerId, GetAsmConfigFromUi());
IsDirty = true; IsDirty = true;
} }
@ -574,31 +626,27 @@ namespace SourceGenWPF.WpfGui {
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private AssemblerConfig GetAsmConfigFromUi() { private AssemblerConfig GetAsmConfigFromUi() {
const int MIN_WIDTH = 1;
const int MAX_WIDTH = 200;
int[] widths = new int[4]; int[] widths = new int[4];
for (int i = 0; i < widths.Length; i++) {
widths[i] = MIN_WIDTH;
}
int result; if (!Validation.GetHasError(asmLabelColWidthTextBox)) {
if (int.TryParse(asmLabelColWidthTextBox.Text, out result) && result >= MIN_WIDTH && widths[0] = AsmLabelColWidth;
result <= MAX_WIDTH) { } else {
widths[0] = result; widths[0] = ASM_COL_MIN_WIDTH;
} }
if (int.TryParse(asmOpcodeColWidthTextBox.Text, out result) && result >= MIN_WIDTH && if (!Validation.GetHasError(asmOpcodeColWidthTextBox)) {
result <= MAX_WIDTH) { widths[1] = AsmOpcodeColWidth;
widths[1] = result; } else {
widths[1] = ASM_COL_MIN_WIDTH;
} }
if (int.TryParse(asmOperandColWidthTextBox.Text, out result) && result >= MIN_WIDTH && if (!Validation.GetHasError(asmOperandColWidthTextBox)) {
result <= MAX_WIDTH) { widths[2] = AsmOperandColWidth;
widths[2] = result; } else {
widths[2] = ASM_COL_MIN_WIDTH;
} }
if (int.TryParse(asmCommentColWidthTextBox.Text, out result) && result >= MIN_WIDTH && if (!Validation.GetHasError(asmCommentColWidthTextBox)) {
result <= MAX_WIDTH) { widths[3] = AsmCommentColWidth;
widths[3] = result; } else {
widths[3] = ASM_COL_MIN_WIDTH;
} }
return new AssemblerConfig(asmExePathTextBox.Text, widths); return new AssemblerConfig(asmExePathTextBox.Text, widths);
@ -691,4 +739,27 @@ namespace SourceGenWPF.WpfGui {
#endregion PseudoOp #endregion PseudoOp
} }
public class AsmColWidthRule : ValidationRule {
public override ValidationResult Validate(object value, CultureInfo cultureInfo) {
// Validating TextBox input, so value should always be a string. Check anyway.
string strValue = Convert.ToString(value);
if (string.IsNullOrEmpty(strValue)) {
//Debug.WriteLine("VVV not string");
return new ValidationResult(false, "Could not convert to string");
}
if (int.TryParse(strValue, out int result)) {
if (result >= EditAppSettings.ASM_COL_MIN_WIDTH &&
result <= EditAppSettings.ASM_COL_MAX_WIDTH) {
return ValidationResult.ValidResult;
}
//Debug.WriteLine("VVV out of range: '" + strValue + "' (" + result + ")");
return new ValidationResult(false, "Column width out of range");
}
//Debug.WriteLine("VVV not valid integer: '" + strValue + "'");
return new ValidationResult(false, "Invalid integer value: '" + strValue + "'");
}
}
} }