1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-27 18:29:30 +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"/>
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal">
<TextBox Name="asmLabelColWidthTextBox" Width="60" Margin="0,0,4,0"
PreviewTextInput="CheckWidthInput"
TextChanged="AsmLabelColWidthTextBox_TextChanged"/>
TextChanged="AsmColWidthTextBox_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"
PreviewTextInput="CheckWidthInput"
TextChanged="AsmLabelColWidthTextBox_TextChanged"/>
TextChanged="AsmColWidthTextBox_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"
PreviewTextInput="CheckWidthInput"
TextChanged="AsmLabelColWidthTextBox_TextChanged"/>
TextChanged="AsmColWidthTextBox_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"
PreviewTextInput="CheckWidthInput"
TextChanged="AsmLabelColWidthTextBox_TextChanged"/>
TextChanged="AsmColWidthTextBox_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)"/>
</StackPanel>
</Grid>

View File

@ -17,11 +17,11 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Microsoft.Win32;
using CommonUtil;
@ -448,44 +448,113 @@ namespace SourceGenWPF.WpfGui {
#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;
public bool ShowCycleCounts {
get { return mShowCycleCounts; }
set {
mShowCycleCounts = value;
OnPropertyChanged();
mSettings.SetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, value);
IsDirty = true;
if (mShowCycleCounts != value) {
mShowCycleCounts = value;
OnPropertyChanged();
mSettings.SetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, value);
IsDirty = true;
}
}
}
private bool mLongLabelNewLine;
public bool LongLabelNewLine {
get { return mLongLabelNewLine; }
set {
mLongLabelNewLine = value;
OnPropertyChanged();
mSettings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, value);
IsDirty = true;
if (mLongLabelNewLine != value) {
mLongLabelNewLine = value;
OnPropertyChanged();
mSettings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, value);
IsDirty = true;
}
}
}
private bool mAddIdentComment;
public bool AddIdentComment {
get { return mAddIdentComment; }
set {
mAddIdentComment = value;
OnPropertyChanged();
mSettings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, value);
IsDirty = true;
if (mAddIdentComment != value) {
mAddIdentComment = value;
OnPropertyChanged();
mSettings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, value);
IsDirty = true;
}
}
}
private bool mDisableLabelLocalization;
public bool DisableLabelLocalization {
get { return mDisableLabelLocalization; }
set {
mDisableLabelLocalization = value;
OnPropertyChanged();
mSettings.SetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, value);
IsDirty = true;
if (mDisableLabelLocalization != value) {
mDisableLabelLocalization = value;
OnPropertyChanged();
mSettings.SetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, value);
IsDirty = true;
}
}
}
@ -535,36 +604,19 @@ namespace SourceGenWPF.WpfGui {
}
/// <summary>
/// Checks whether the character typed into a column width entry field is allowed.
/// 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.
/// Updates the assembler config settings whenever one of the width text fields is edited.
/// </summary>
/// <remarks>
/// 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>
private void AsmLabelColWidthTextBox_TextChanged(object sender, TextChangedEventArgs e) {
private void AsmColWidthTextBox_TextChanged(object sender, TextChangedEventArgs e) {
AssemblerInfo asm = (AssemblerInfo)asmConfigComboBox.SelectedItem;
if (asm == null) {
// fires during dialog initialization, before anything is selected
return;
}
AssemblerConfig.SetConfig(mSettings, asm.AssemblerId, GetAsmConfigFromUi());
IsDirty = true;
}
@ -574,31 +626,27 @@ namespace SourceGenWPF.WpfGui {
/// </summary>
/// <returns></returns>
private AssemblerConfig GetAsmConfigFromUi() {
const int MIN_WIDTH = 1;
const int MAX_WIDTH = 200;
int[] widths = new int[4];
for (int i = 0; i < widths.Length; i++) {
widths[i] = MIN_WIDTH;
}
int result;
if (int.TryParse(asmLabelColWidthTextBox.Text, out result) && result >= MIN_WIDTH &&
result <= MAX_WIDTH) {
widths[0] = result;
if (!Validation.GetHasError(asmLabelColWidthTextBox)) {
widths[0] = AsmLabelColWidth;
} else {
widths[0] = ASM_COL_MIN_WIDTH;
}
if (int.TryParse(asmOpcodeColWidthTextBox.Text, out result) && result >= MIN_WIDTH &&
result <= MAX_WIDTH) {
widths[1] = result;
if (!Validation.GetHasError(asmOpcodeColWidthTextBox)) {
widths[1] = AsmOpcodeColWidth;
} else {
widths[1] = ASM_COL_MIN_WIDTH;
}
if (int.TryParse(asmOperandColWidthTextBox.Text, out result) && result >= MIN_WIDTH &&
result <= MAX_WIDTH) {
widths[2] = result;
if (!Validation.GetHasError(asmOperandColWidthTextBox)) {
widths[2] = AsmOperandColWidth;
} else {
widths[2] = ASM_COL_MIN_WIDTH;
}
if (int.TryParse(asmCommentColWidthTextBox.Text, out result) && result >= MIN_WIDTH &&
result <= MAX_WIDTH) {
widths[3] = result;
if (!Validation.GetHasError(asmCommentColWidthTextBox)) {
widths[3] = AsmCommentColWidth;
} else {
widths[3] = ASM_COL_MIN_WIDTH;
}
return new AssemblerConfig(asmExePathTextBox.Text, widths);
@ -691,4 +739,27 @@ namespace SourceGenWPF.WpfGui {
#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 + "'");
}
}
}