1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-25 05:29:31 +00:00

Load/save app settings

This change pulls in the settings file code, which is mostly
unchanged except when it comes to saving and restoring the window
location and size.

The old system has been replaced with a PInvoke-based version that
calls the underlying Win32 window placement code.  This is more
likely to be correct when multiple displays are in use, and can
record the un-maximized size of a maximized window.  It leaves a
nasty XML string embedded in the config file, but it's not really
meant to be human-readable anyway.

The sub-window dividers all work completely differently from the way
they did in WinForms, and some of the behavior is a bit obscure
(like noticing when a splitter moves due to keyboard input, and
setting the position in a way that doesn't break the auto-sizing).
Yay WPF.

Still need to preserve column widths.
This commit is contained in:
Andy McFadden 2019-06-19 16:31:56 -07:00
parent 7339d2a681
commit 96a92f0335
9 changed files with 444 additions and 86 deletions

View File

@ -63,6 +63,7 @@
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Compile Include="WindowPlacement.cs" />
<Compile Include="WPFExtensions.cs" />
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>

View File

@ -36,8 +36,8 @@ namespace CommonWPF {
/// From https://social.msdn.microsoft.com/Forums/vstudio/en-US/7d0626cb-67e8-4a09-a01e-8e56ee7411b2/gridviewcolumheader-radiobuttons?forum=wpf
/// </remarks>
/// <typeparam name="T"></typeparam>
/// <param name="referenceVisual"></param>
/// <returns></returns>
/// <param name="referenceVisual">Start point.</param>
/// <returns>Object of appropriate type, or null if not found.</returns>
public static T GetVisualChild<T>(this Visual referenceVisual) where T : Visual {
Visual child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceVisual); i++) {

View File

@ -0,0 +1,126 @@
/*
* This comes from "David Rickard's Tech Blog", posted by RandomEngy on March 8 2010:
* https://blogs.msdn.microsoft.com/davidrickard/2010/03/08/saving-window-size-and-location-in-wpf-and-winforms/
*
* Saving and restoring a window's size and position can be tricky when there are multiple
* displays involved. This uses the Win32 system functions to do the job properly and
* consistently. (In theory.)
*
* The code works for WinForms (save on FormClosing, restore on Load, using the native handle
* from the Handle property) and WPF (use the Window extension methods in Closing and
* SourceInitialized). Besides convenience, it has the added benefit of being able to
* capture the non-maximized values for a maximized window.
*/
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Xml;
using System.Xml.Serialization;
namespace CommonWPF {
// RECT structure required by WINDOWPLACEMENT structure
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(int left, int top, int right, int bottom) {
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
}
// POINT structure required by WINDOWPLACEMENT structure
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct POINT {
public int X;
public int Y;
public POINT(int x, int y) {
this.X = x;
this.Y = y;
}
}
// WINDOWPLACEMENT stores the position, size, and state of a window
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT {
public int length;
public int flags;
public int showCmd;
public POINT minPosition;
public POINT maxPosition;
public RECT normalPosition;
}
public static class WindowPlacement {
private static Encoding encoding = new UTF8Encoding();
private static XmlSerializer serializer = new XmlSerializer(typeof(WINDOWPLACEMENT));
[DllImport("user32.dll")]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll")]
private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
public static void SetPlacement(IntPtr windowHandle, string placementXml) {
if (string.IsNullOrEmpty(placementXml)) {
return;
}
WINDOWPLACEMENT placement;
byte[] xmlBytes = encoding.GetBytes(placementXml);
try {
using (MemoryStream memoryStream = new MemoryStream(xmlBytes)) {
placement = (WINDOWPLACEMENT)serializer.Deserialize(memoryStream);
}
placement.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
placement.flags = 0;
placement.showCmd = (placement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : placement.showCmd);
SetWindowPlacement(windowHandle, ref placement);
} catch (InvalidOperationException) {
// Parsing placement XML failed. Fail silently.
}
}
public static string GetPlacement(IntPtr windowHandle) {
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
GetWindowPlacement(windowHandle, out placement);
using (MemoryStream memoryStream = new MemoryStream()) {
using (XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8)) {
serializer.Serialize(xmlTextWriter, placement);
byte[] xmlBytes = memoryStream.ToArray();
return encoding.GetString(xmlBytes);
}
}
}
//
// Extension methods for WPF.
//
public static string GetPlacement(this Window window) {
return GetPlacement(new WindowInteropHelper(window).Handle);
}
public static void SetPlacement(this Window window, string placementXml) {
SetPlacement(new WindowInteropHelper(window).Handle, placementXml);
}
}
}

View File

@ -37,15 +37,11 @@ namespace SourceGenWPF {
// makes it easy to find all uses.
// Main window.
public const string MAIN_WINDOW_WIDTH = "main-window-width";
public const string MAIN_WINDOW_HEIGHT = "main-window-height";
public const string MAIN_WINDOW_LOC_X = "main-window-loc-x";
public const string MAIN_WINDOW_LOC_Y = "main-window-loc-y";
public const string MAIN_WINDOW_MAXIMIZED = "main-window-maximized";
public const string MAIN_WINDOW_PLACEMENT = "main-window-placement";
public const string MAIN_LEFT_PANEL_WIDTH = "main-left-panel-width";
public const string MAIN_RIGHT_PANEL_WIDTH = "main-right-panel-width";
public const string MAIN_LEFT_SIDE_SPLITTER_DIST = "main-left-side-splitter-dist";
public const string MAIN_RIGHT_SIDE_SPLITTER_DIST = "main-right-side-splitter-dist";
public const string MAIN_REFERENCES_HEIGHT = "main-references-height";
public const string MAIN_SYMBOLS_HEIGHT = "main-symbols-height";
// New project dialog.
public const string NEWP_SELECTED_SYSTEM = "newp-selected-system";

View File

@ -24,7 +24,9 @@ using System.Windows;
using Asm65;
using CommonUtil;
using CommonWPF;
using SourceGenWPF.ProjWin;
using SourceGenWPF.Sandbox;
namespace SourceGenWPF {
/// <summary>
@ -35,6 +37,8 @@ namespace SourceGenWPF {
/// There is some Windows-specific stuff, like MessageBox and OpenFileDialog.
/// </summary>
public class MainController {
private const string SETTINGS_FILE_NAME = "SourceGen-settings";
#region Project state
// Currently open project, or null if none.
@ -46,14 +50,6 @@ namespace SourceGenWPF {
// Pathname of .dis65 file. This will be empty for a new project.
private string mProjectPathName;
#if false
/// <summary>
/// Symbol subset, used to supply data to the symbol ListView. Initialized with
/// an empty symbol table.
/// </summary>
private SymbolTableSubset mSymbolSubset;
#endif
/// <summary>
/// Data backing the code list.
/// </summary>
@ -155,6 +151,18 @@ namespace SourceGenWPF {
mMainWin = win;
}
/// <summary>
/// Early initialization, before the window is visible. Notably, we want to get the
/// window placement data, so we can position and size the window before it's first
/// drawn (avoids a blink).
/// </summary>
public void WindowSourceInitialized() {
// Load the settings from the file. If this fails we have no way to tell the user,
// so just keep going.
LoadAppSettings();
SetAppWindowLocation();
}
/// <summary>
/// Perform one-time initialization after the Window has finished loading. We defer
/// to this point so we can report fatal errors directly to the user.
@ -167,7 +175,6 @@ namespace SourceGenWPF {
Application.Current.Shutdown();
return;
}
#if false
try {
PluginDllCache.PreparePluginDir();
} catch (Exception ex) {
@ -175,61 +182,20 @@ namespace SourceGenWPF {
if (pluginPath == null) {
pluginPath = "<???>";
}
string msg = string.Format(Properties.Resources.PLUGIN_DIR_FAIL,
string msg = string.Format(Res.Strings.PLUGIN_DIR_FAIL_FMT,
pluginPath + ": " + ex.Message);
MessageBox.Show(this, msg, Properties.Resources.PLUGIN_DIR_FAIL_CAPTION,
MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
MessageBox.Show(msg, Res.Strings.PLUGIN_DIR_FAIL_CAPTION,
MessageBoxButton.OK, MessageBoxImage.Error);
Application.Current.Shutdown();
return;
}
#endif
#if false
logoPictureBox.ImageLocation = RuntimeDataAccess.GetPathName(LOGO_FILE_NAME);
versionLabel.Text = string.Format(Properties.Resources.VERSION_FMT,
Program.ProgramVersion);
toolStripStatusLabel.Text = Properties.Resources.STATUS_READY;
mProjectControl = this.codeListView;
mNoProjectControl = this.noProjectPanel;
// Clone the menu structure from the designer. The same items are used for
// both Edit > Actions and the right-click context menu in codeListView.
mActionsMenuItems = new ToolStripItem[actionsToolStripMenuItem.DropDownItems.Count];
for (int i = 0; i < actionsToolStripMenuItem.DropDownItems.Count; i++) {
mActionsMenuItems[i] = actionsToolStripMenuItem.DropDownItems[i];
}
#endif
#if false
// Load the settings from the file. Some things (like the symbol subset) need
// these. The general "apply settings" doesn't happen until a bit later, after
// the sub-windows have been initialized.
LoadAppSettings();
// Init primary ListView (virtual, ownerdraw)
InitCodeListView();
// Init Symbols ListView (virtual, non-ownerdraw)
mSymbolSubset = new SymbolTableSubset(new SymbolTable());
symbolListView.SetDoubleBuffered(true);
InitSymbolListView();
// Init References ListView (non-virtual, non-ownerdraw)
referencesListView.SetDoubleBuffered(true);
// Place the main window and apply the various settings.
SetAppWindowLocation();
#endif
ApplyAppSettings();
#if false
UpdateActionMenu();
UpdateMenuItemsAndTitle();
UpdateRecentLinks();
ShowNoProject();
#endif
ProcessCommandLine();
@ -242,6 +208,163 @@ namespace SourceGenWPF {
}
}
/// <summary>
/// Loads settings from the settings file into AppSettings.Global. Does not apply
/// them to the ProjectView.
/// </summary>
private void LoadAppSettings() {
AppSettings settings = AppSettings.Global;
// Set some default settings for first-time use. The general rule is to set
// a default value of false, 0, or the empty string, so we only need to set
// values here when that isn't the case. The point at which the setting is
// actually used is expected to do something reasonable by default.
settings.SetBool(AppSettings.SYMWIN_SHOW_USER, true);
settings.SetBool(AppSettings.SYMWIN_SHOW_PROJECT, true);
settings.SetBool(AppSettings.SYMWIN_SHOW_PLATFORM, false);
settings.SetBool(AppSettings.SYMWIN_SHOW_AUTO, false);
settings.SetBool(AppSettings.SYMWIN_SHOW_CONST, true);
settings.SetBool(AppSettings.SYMWIN_SHOW_ADDR, true);
settings.SetBool(AppSettings.SYMWIN_SORT_ASCENDING, true);
settings.SetInt(AppSettings.SYMWIN_SORT_COL, (int)Symbol.SymbolSortField.Name);
settings.SetBool(AppSettings.FMT_UPPER_OPERAND_A, true);
settings.SetBool(AppSettings.FMT_UPPER_OPERAND_S, true);
settings.SetBool(AppSettings.FMT_ADD_SPACE_FULL_COMMENT, true);
settings.SetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, "l");
settings.SetString(AppSettings.FMT_OPERAND_PREFIX_ABS, "a:");
settings.SetString(AppSettings.FMT_OPERAND_PREFIX_LONG, "f:");
settings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, true);
settings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, true);
#if DEBUG
settings.SetBool(AppSettings.DEBUG_MENU_ENABLED, true);
#else
settings.SetBool(AppSettings.DEBUG_MENU_ENABLED, false);
#endif
// Load the settings file, and merge it into the globals.
string runtimeDataDir = RuntimeDataAccess.GetDirectory();
if (runtimeDataDir == null) {
Debug.WriteLine("Unable to load settings file");
return;
}
string settingsDir = Path.GetDirectoryName(runtimeDataDir);
string settingsPath = Path.Combine(settingsDir, SETTINGS_FILE_NAME);
try {
string text = File.ReadAllText(settingsPath);
AppSettings fileSettings = AppSettings.Deserialize(text);
AppSettings.Global.MergeSettings(fileSettings);
Debug.WriteLine("Settings file loaded and merged");
} catch (Exception ex) {
Debug.WriteLine("Unable to read settings file: " + ex.Message);
}
}
/// <summary>
/// Saves AppSettings to a file.
/// </summary>
private void SaveAppSettings() {
if (!AppSettings.Global.Dirty) {
Debug.WriteLine("Settings not dirty, not saving");
return;
}
// Main window position and size.
AppSettings.Global.SetString(AppSettings.MAIN_WINDOW_PLACEMENT,
mMainWin.GetPlacement());
// Horizontal splitters.
AppSettings.Global.SetInt(AppSettings.MAIN_LEFT_PANEL_WIDTH,
(int)mMainWin.LeftPanelWidth);
AppSettings.Global.SetInt(AppSettings.MAIN_RIGHT_PANEL_WIDTH,
(int)mMainWin.RightPanelWidth);
// Vertical splitters.
AppSettings.Global.SetInt(AppSettings.MAIN_REFERENCES_HEIGHT,
(int)mMainWin.ReferencesPanelHeight);
AppSettings.Global.SetInt(AppSettings.MAIN_SYMBOLS_HEIGHT,
(int)mMainWin.SymbolsPanelHeight);
#if false
SerializeCodeListColumnWidths();
SerializeReferencesColumnWidths();
SerializeNotesColumnWidths();
SerializeSymbolColumnWidths();
#endif
string runtimeDataDir = RuntimeDataAccess.GetDirectory();
if (runtimeDataDir == null) {
Debug.WriteLine("Unable to save settings file");
return;
}
string settingsDir = Path.GetDirectoryName(runtimeDataDir);
string settingsPath = Path.Combine(settingsDir, SETTINGS_FILE_NAME);
try {
string cereal = AppSettings.Global.Serialize();
File.WriteAllText(settingsPath, cereal);
AppSettings.Global.Dirty = false;
Debug.WriteLine("Saved settings (" + settingsPath + ")");
} catch (Exception ex) {
Debug.WriteLine("Failed to save settings: " + ex.Message);
}
}
/// <summary>
/// Replaces the contents of the global settings object with the new settings,
/// then applies them to the project.
/// </summary>
/// <param name="settings"></param>
public void SetAppSettings(AppSettings settings) {
AppSettings.Global.ReplaceSettings(settings);
ApplyAppSettings();
// We get called whenever Apply or OK is hit in the settings editor, so it's
// a pretty good time to save the settings out.
SaveAppSettings();
}
/// <summary>
/// Sets the app window's location and size. This should be called before the window has
/// finished initialization.
/// </summary>
private void SetAppWindowLocation() {
const int DEFAULT_SPLIT = 250;
AppSettings settings = AppSettings.Global;
string placement = settings.GetString(AppSettings.MAIN_WINDOW_PLACEMENT, null);
if (placement != null) {
mMainWin.SetPlacement(placement);
}
mMainWin.LeftPanelWidth =
settings.GetInt(AppSettings.MAIN_LEFT_PANEL_WIDTH, DEFAULT_SPLIT);
mMainWin.RightPanelWidth =
settings.GetInt(AppSettings.MAIN_RIGHT_PANEL_WIDTH, DEFAULT_SPLIT);
mMainWin.ReferencesPanelHeight =
settings.GetInt(AppSettings.MAIN_REFERENCES_HEIGHT, 350);
mMainWin.SymbolsPanelHeight =
settings.GetInt(AppSettings.MAIN_SYMBOLS_HEIGHT, 400);
#if false
// Configure column widths.
string widthStr = settings.GetString(AppSettings.CDLV_COL_WIDTHS, null);
if (!string.IsNullOrEmpty(widthStr)) {
CodeListColumnWidths widths = CodeListColumnWidths.Deserialize(widthStr);
if (widths != null) {
SetCodeListHeaderWidths(widths);
}
}
DeserializeReferencesColumnWidths();
DeserializeNotesColumnWidths();
DeserializeSymbolColumnWidths();
#endif
}
/// <summary>
/// Applies "actionable" settings to the ProjectView, pulling them out of the global
/// settings object. If a project is open, refreshes the display list and all sub-windows.
@ -363,8 +486,6 @@ namespace SourceGenWPF {
#endregion Init and settings
#region Project management
private bool PrepareNewProject(string dataPathName, SystemDef sysDef) {
@ -891,14 +1012,21 @@ namespace SourceGenWPF {
// Update this, in case this was a new project.
UpdateRecentProjectList(pathName);
#if false
// Seems like a good time to save this off too.
SaveAppSettings();
#endif
return true;
}
/// <summary>
/// Handles main window closing.
/// </summary>
/// <returns>True if it's okay for the window to close, false to cancel it.</returns>
public bool WindowClosing() {
SaveAppSettings();
return CloseProject();
}
/// <summary>
/// Closes the project and associated modeless dialogs. Unsaved changes will be
/// lost, so if the project has outstanding changes the user will be given the

View File

@ -24,8 +24,12 @@ limitations under the License.
Title="6502bench SourceGen"
Icon="/SourceGenWPF;component/Res/SourceGenIcon.ico"
Width="1000" Height="600" MinWidth="800" MinHeight="500"
SourceInitialized="Window_SourceInitialized"
Loaded="Window_Loaded"
MouseDown="Window_MouseDown">
LocationChanged="Window_LocationChanged"
SizeChanged="Window_SizeChanged"
MouseDown="Window_MouseDown"
Closing="Window_Closing">
<Window.Resources>
<ResourceDictionary>
@ -245,12 +249,12 @@ limitations under the License.
</Grid.ColumnDefinitions>
<GridSplitter Width="4" Grid.Column="1" HorizontalAlignment="Left"/>
<GridSplitter Width="4" Grid.Column="3" HorizontalAlignment="Center"/>
<GridSplitter Name="leftSplitter" Width="4" Grid.Column="1" HorizontalAlignment="Left"/>
<GridSplitter Name="rightSplitter" Width="4" Grid.Column="3" HorizontalAlignment="Center"/>
<Grid Name="leftPanel" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="100"/>
<RowDefinition Height="2*" MinHeight="100"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" MinHeight="100"/>
</Grid.RowDefinitions>
@ -374,9 +378,9 @@ limitations under the License.
<Grid Name="rightPanel" Grid.Column="4">
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="100"/>
<RowDefinition Height="2*" MinHeight="100"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="143" MinHeight="100"/>
<RowDefinition Height="*" MinHeight="100"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="Symbols">
@ -430,7 +434,7 @@ limitations under the License.
</GroupBox>
<GridSplitter Height="4" Grid.Row="1"
HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
</Grid>
</Grid>
</DockPanel>

View File

@ -77,6 +77,20 @@ namespace SourceGenWPF.ProjWin {
AddMultiKeyGestures();
// Get an event when the splitters move. Because of the way things are set up, it's
// actually best to get an event when the grid row/column sizes change.
// https://stackoverflow.com/a/22495586/294248
DependencyPropertyDescriptor widthDesc = DependencyPropertyDescriptor.FromProperty(
ColumnDefinition.WidthProperty, typeof(ItemsControl));
DependencyPropertyDescriptor heightDesc = DependencyPropertyDescriptor.FromProperty(
RowDefinition.HeightProperty, typeof(ItemsControl));
// main window, left/right panels
widthDesc.AddValueChanged(triptychGrid.ColumnDefinitions[0], GridSizeChanged);
widthDesc.AddValueChanged(triptychGrid.ColumnDefinitions[4], GridSizeChanged);
// references vs. notes
heightDesc.AddValueChanged(leftPanel.RowDefinitions[0], GridSizeChanged);
heightDesc.AddValueChanged(rightPanel.RowDefinitions[0], GridSizeChanged);
//GridView gv = (GridView)codeListView.View;
//gv.Columns[0].Width = 50;
}
@ -110,18 +124,6 @@ namespace SourceGenWPF.ProjWin {
}));
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
mMainCtrl.WindowLoaded();
CreateCodeListContextMenu();
#if DEBUG
// Get more info on CollectionChanged events that do not agree with current
// state of Items collection.
PresentationTraceSources.SetTraceLevel(codeListView.ItemContainerGenerator,
PresentationTraceLevel.High);
#endif
}
private void CreateCodeListContextMenu() {
// Find Actions menu.
ItemCollection mainItems = this.appMenu.Items;
@ -206,6 +208,46 @@ namespace SourceGenWPF.ProjWin {
get { return mShowCodeListView ? Visibility.Visible : Visibility.Hidden; }
}
/// <summary>
/// Handles source-initialized event. This happens before Loaded, before the window
/// is visible, which makes it a good time to set the size and position.
/// </summary>
private void Window_SourceInitialized(object sender, EventArgs e) {
mMainCtrl.WindowSourceInitialized();
}
/// <summary>
/// Handles window-loaded event. Window is ready to go, so we can start doing things
/// that involve user interaction.
/// </summary>
private void Window_Loaded(object sender, RoutedEventArgs e) {
mMainCtrl.WindowLoaded();
CreateCodeListContextMenu();
#if DEBUG
// Get more info on CollectionChanged events that do not agree with current
// state of Items collection.
PresentationTraceSources.SetTraceLevel(codeListView.ItemContainerGenerator,
PresentationTraceLevel.High);
#endif
}
/// <summary>
/// Handles window-close event. The user has an opportunity to cancel.
/// </summary>
private void Window_Closing(object sender, CancelEventArgs e) {
Debug.WriteLine("Main app window closing");
if (mMainCtrl == null) {
// early failure?
return;
}
if (!mMainCtrl.WindowClosing()) {
e.Cancel = true;
return;
}
}
/// <summary>
/// Catch mouse-down events so we can treat the fourth mouse button as "back".
/// </summary>
@ -215,6 +257,61 @@ namespace SourceGenWPF.ProjWin {
}
}
#region Window placement
//
// We record the location and size of the window, and the sizes of the panels, in
// the settings file. All we need to do here is note that something has changed.
//
private void Window_LocationChanged(object sender, EventArgs e) {
Debug.WriteLine("Main window location changed");
AppSettings.Global.Dirty = true;
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e) {
Debug.WriteLine("Main window size changed");
AppSettings.Global.Dirty = true;
}
private void GridSizeChanged(object sender, EventArgs e) {
Debug.WriteLine("Splitter size change");
AppSettings.Global.Dirty = true;
}
public double LeftPanelWidth {
get { return triptychGrid.ColumnDefinitions[0].ActualWidth; }
set { triptychGrid.ColumnDefinitions[0].Width = new GridLength(value); }
}
public double RightPanelWidth {
get { return triptychGrid.ColumnDefinitions[4].ActualWidth; }
set { triptychGrid.ColumnDefinitions[4].Width = new GridLength(value); }
}
public double ReferencesPanelHeight {
get { return leftPanel.RowDefinitions[0].ActualHeight; }
set {
// If you set the height to a pixel value, you lose the auto-sizing behavior,
// and the splitter will happily shove the bottom panel off the bottom of the
// main window. The trick is to use "star" units.
// Thanks: https://stackoverflow.com/q/35000893/294248
double totalHeight = leftPanel.RowDefinitions[0].ActualHeight +
leftPanel.RowDefinitions[2].ActualHeight;
leftPanel.RowDefinitions[0].Height = new GridLength(value, GridUnitType.Star);
leftPanel.RowDefinitions[2].Height = new GridLength(totalHeight - value,
GridUnitType.Star);
}
}
public double SymbolsPanelHeight {
get { return rightPanel.RowDefinitions[0].ActualHeight; }
set {
double totalHeight = rightPanel.RowDefinitions[0].ActualHeight +
rightPanel.RowDefinitions[2].ActualHeight;
rightPanel.RowDefinitions[0].Height = new GridLength(value, GridUnitType.Star);
rightPanel.RowDefinitions[2].Height = new GridLength(totalHeight - value,
GridUnitType.Star);
}
}
#endregion Window placement
/// <summary>
/// Sets the focus on the code list.
/// </summary>

View File

@ -61,6 +61,8 @@ limitations under the License.
<system:String x:Key="str_OpenDataWrongLengthFmt">The file is {0:N0} bytes long, but the project expected {1:N0}.</system:String>
<system:String x:Key="str_OpenDataWrongCrcFmt">The file has CRC {0}, but the project expected {1}.</system:String>
<system:String x:Key="str_OperationFailed">Failed</system:String>
<system:String x:Key="str_PluginDirFailFmt">Failed while preparing the plugin directory {0}</system:String>
<system:String x:Key="str_PluginDirFailCaption">Failed Preparing Plugin Directory</system:String>
<system:String x:Key="str_ProgressAssembling">Executing assembler...</system:String>
<system:String x:Key="str_ProgressGeneratingFmt">Generating {0}...</system:String>
<system:String x:Key="str_ProjectFieldComment">comment</system:String>

View File

@ -102,6 +102,10 @@ namespace SourceGenWPF.Res {
(string)Application.Current.FindResource("str_OpenDataWrongLengthFmt");
public static string OPERATION_FAILED =
(string)Application.Current.FindResource("str_OperationFailed");
public static string PLUGIN_DIR_FAIL_FMT =
(string)Application.Current.FindResource("str_PluginDirFailFmt");
public static string PLUGIN_DIR_FAIL_CAPTION =
(string)Application.Current.FindResource("str_PluginDirFailCaption");
public static string PROGRESS_ASSEMBLING =
(string)Application.Current.FindResource("str_ProgressAssembling");
public static string PROGRESS_GENERATING_FMT =