public Section(MonitorLayout _layout, RECT _rect) { layout = _layout; rect = _rect; }
// Methods public fts_winsnap() { // Set working dir to exe dir so app don't crash on startup var exePath = System.IO.Path.GetDirectoryName(Application.ExecutablePath); bool autoStarted = Environment.CurrentDirectory != exePath; if (autoStarted) { Environment.CurrentDirectory = exePath; } // Read settings from disk (if there are any) LoadSettings(); // Check registry for startup option var registryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); var v = registryKey.GetValue("fts_winsnap") as string; if (v == Application.ExecutablePath) { _runOnStartup = true; } // WinForm Component initialization InitializeComponent(); this._notifyIcon.ContextMenu = new System.Windows.Forms.ContextMenu(new System.Windows.Forms.MenuItem[] { new System.Windows.Forms.MenuItem("Show fts_winsnap", (object sender, System.EventArgs e) => { this?.Show(); }), new System.Windows.Forms.MenuItem("Close fts_winsnap", (object sender, System.EventArgs e) => { this?.Close(); }) }); Application.ApplicationExit += new EventHandler(this.OnApplicationExit); // Minimize to task bar if app was automatically launched by Windows on startup if (autoStarted) { this.ShowInTaskbar = false; this.WindowState = FormWindowState.Minimized; } // Register hotkeys RegisterHotKey(this.Handle, 0, Constants.WM_MOD_CTRL | Constants.WM_MOD_ALT, (int)Keys.Left); RegisterHotKey(this.Handle, 1, Constants.WM_MOD_CTRL | Constants.WM_MOD_ALT, (int)Keys.Right); RegisterHotKey(this.Handle, 2, Constants.WM_MOD_CTRL | Constants.WM_MOD_ALT, (int)Keys.Up); RegisterHotKey(this.Handle, 3, Constants.WM_MOD_CTRL | Constants.WM_MOD_ALT, (int)Keys.Down); RegisterHotKey(this.Handle, 4, Constants.WM_MOD_CTRL | Constants.WM_MOD_SHIFT | Constants.WM_MOD_ALT, (int)Keys.Down); RegisterHotKey(this.Handle, 5, Constants.WM_MOD_CTRL | Constants.WM_MOD_SHIFT | Constants.WM_MOD_ALT, (int)Keys.Left); RegisterHotKey(this.Handle, 6, Constants.WM_MOD_CTRL | Constants.WM_MOD_SHIFT | Constants.WM_MOD_ALT, (int)Keys.Right); RegisterHotKey(this.Handle, 7, Constants.WM_MOD_CTRL | Constants.WM_MOD_SHIFT | Constants.WM_MOD_ALT, (int)Keys.Up); // Initialize fonts for UI var helveticaFamily = new FontFamily("Helvetica"); var monospaceFamily = new FontFamily(System.Drawing.Text.GenericFontFamilies.Monospace); var pfc = new System.Drawing.Text.PrivateFontCollection(); pfc.AddFontFile(@"Karla-Regular.ttf"); var karlaFamily = pfc.Families[0]; // Initialize per-monitor settings while (_settings.monitorSettings.Count < Screen.AllScreens.Count()) { var newSettings = new Settings.MonitorSettings(); _settings.monitorSettings.Add(newSettings); } // createLabel helper Func <string, FontFamily, float, FontStyle, Padding, Rectangle, Label> createLabel = (string text, FontFamily fontFamily, float fontScale, FontStyle fontStyle, Padding padding, Rectangle bounds) => { var newLabel = new Label(); newLabel.Text = text; newLabel.Font = new Font(fontFamily, newLabel.Font.Size * fontScale, fontStyle); newLabel.AutoSize = true; newLabel.Padding = padding; newLabel.Bounds = bounds; this.Controls.Add(newLabel); return(newLabel); }; // Create UI for each monitor int idx = 1; int y = 0; foreach (var monitor in Screen.AllScreens) { // Create a new layout for this monitor var newLayout = new MonitorLayout(monitor); _layouts.Add(newLayout); // Grab settings for this monitor var monitorSettings = _settings.monitorSettings[idx - 1]; // Monitor # var titleLabel = createLabel("Monitor " + idx.ToString(), karlaFamily, 2f, FontStyle.Bold, new Padding(4), new Rectangle(0, y, 0, 0)); // Resolution: 1920x1080 var resolutionLabel = createLabel("Resolution: " + monitor.Bounds.Width.ToString() + "x" + monitor.Bounds.Height.ToString(), karlaFamily, 1.5f, FontStyle.Regular, new Padding(32, 3, 3, 3), new Rectangle(titleLabel.Bounds.Left, titleLabel.Bounds.Bottom, 0, 0)); // Resize: <NumberField> var resizeLabel = createLabel("Resize: ", karlaFamily, 1.5f, FontStyle.Regular, new Padding(32, 0, 0, 0), new Rectangle(0, resolutionLabel.Bottom, 0, 0)); var resizeControl = new NumericUpDown(); resizeControl.Bounds = new Rectangle(resizeLabel.Bounds.Right + 4, resizeLabel.Bounds.Top, 50, 0); resizeControl.Minimum = -30; resizeControl.Maximum = 30; resizeControl.Value = monitorSettings.adjustSize; resizeControl.AutoSize = true; resizeControl.TabStop = false; resizeControl.ValueChanged += (object sender, EventArgs e) => { // Update settings newLayout.adjustSize = (int)resizeControl.Value; monitorSettings.adjustSize = newLayout.adjustSize; SaveSettings(); }; this.Controls.Add(resizeControl); // Layout plus predefined buttons var layout = createLabel("Layout: ", karlaFamily, 1.5f, FontStyle.Regular, new Padding(32, 8, 3, 3), new Rectangle(resizeLabel.Bounds.Left, resizeLabel.Bounds.Bottom + 5, 0, 0)); var customField = new System.Windows.Forms.TextBox(); Button selectedBtn = null; Button customBtn = null; // Helper to style button visuals Action <Button> styleButton = (Button b) => { b.Padding = new Padding(3); b.AutoSize = true; b.Font = new Font(helveticaFamily, b.Font.Size * 1.2f, FontStyle.Bold); b.FlatStyle = FlatStyle.Flat; b.FlatAppearance.BorderSize = 1; b.FlatAppearance.BorderColor = midGray; b.FlatAppearance.MouseOverBackColor = medBlue; b.FlatAppearance.MouseDownBackColor = medBlue; b.FlatAppearance.CheckedBackColor = darkBlue; b.ForeColor = darkGray; b.BackColor = faintGray; }; // Helper to set layous from json (throw exception on bad json!) Action <string> UpdateLayouts = (string json) => { var layoutEntries = JsonConvert.DeserializeObject <IList <IList <int> > >(json); List <Rectangle> newRects = new List <Rectangle>(layoutEntries.Count); foreach (var layoutEntry in layoutEntries) { int minX = layoutEntry[0]; int minY = layoutEntry[1]; int maxX = layoutEntry[2]; int maxY = layoutEntry[3]; newRects.Add(new Rectangle(minX, minY, maxX - minX, maxY - minY)); } newLayout.SetLayouts(newRects); }; // Handler for when user clicks button Action <Button, string> clickButton = (Button b, string json) => { // Update layouts try { UpdateLayouts(json); } catch (System.Exception /*ex*/) { return; } // Show/Hide json field if 'Custom' button is clicked if (b == customBtn) { customField.Show(); } else { customField.Hide(); } // Reset colors on previously selected button if (selectedBtn != null) { selectedBtn.ForeColor = darkGray; selectedBtn.BackColor = faintGray; } // Set colors on nwely selected button b.BackColor = darkBlue; b.ForeColor = Color.White; selectedBtn = b; // Update settings monitorSettings.selectedButton = b.Text; SaveSettings(); }; Button initialButton = null; string initialJson = null; // Helper to make a button Func <Rectangle, string, string, Button> makeButton = (Rectangle bounds, string label, string json) => { // Create button var newBtn = new System.Windows.Forms.Button(); newBtn.Bounds = bounds; newBtn.Text = label; styleButton(newBtn); Action <object, EventArgs> onClick = (object s, EventArgs e) => { clickButton(newBtn, json); }; newBtn.Click += (object s, EventArgs e) => { onClick(s, e); }; this.Controls.Add(newBtn); // Check if this is the default selection if (label == monitorSettings.selectedButton) { initialButton = newBtn; initialJson = json; } return(newBtn); }; Button btn = null; int pad = 3; btn = makeButton(new Rectangle(layout.Bounds.Right, layout.Bounds.Top, 0, 0), "1x1", "[[0,0,100,100]]"); btn = makeButton(new Rectangle(btn.Bounds.Right + pad, btn.Bounds.Top, 0, 0), "2x1", "[[0,0,50,100], [50,0,100,100]]"); btn = makeButton(new Rectangle(btn.Bounds.Right + pad, btn.Bounds.Top, 0, 0), "1x2", "[[0,0,100,50], [0,50,100,100]]"); btn = makeButton(new Rectangle(btn.Bounds.Right + pad, btn.Bounds.Top, 0, 0), "2x2", "[[0,0,50,50], [50,0,100,50], [0,50,50,100], [50,50,100,100]]"); btn = makeButton(new Rectangle(layout.Bounds.Right, btn.Bounds.Bottom + 5, 0, 0), "3x1", "[[0,0,33,100], [33,0,67,100], [67,0,100,100]]"); btn = makeButton(new Rectangle(btn.Bounds.Right + pad, btn.Bounds.Top, 0, 0), "1x3", "[[0,0,100,33], [0,33,100,67], [0,67,100,100]]"); btn = makeButton(new Rectangle(btn.Bounds.Right + pad, btn.Bounds.Top, 0, 0), "3x3", "[[0,0,33,33], [33,0,67,33], [67,0,100,33], [0,33,33,67], [33,33,67,67], [67,33,100,67], [0,67,33,100], [33,67,67,100], [67,67,100,100]]"); customBtn = makeButton(new Rectangle(btn.Bounds.Right + pad, btn.Bounds.Top, 0, 0), "Custom", monitorSettings.customJson); // Custom field holds JSON layout data. Can be hand edited by user if 'Custom' button is selected customField.Bounds = new Rectangle(layout.Bounds.Right, btn.Bounds.Bottom + 4, this.Width / 2, layout.Bounds.Height); customField.TabStop = false; customField.Font = new Font(monospaceFamily, customField.Font.Size, FontStyle.Regular); customField.Text = monitorSettings.customJson; customField.TextChanged += (object sender, EventArgs e) => { try { UpdateLayouts(customField.Text); customField.BackColor = Color.White; monitorSettings.customJson = customField.Text; SaveSettings(); } catch (Exception /*ex*/) { customField.BackColor = paleRed; } }; this.Controls.Add(customField); // Click initial button for initial state clickButton(initialButton, initialJson); y = customField.Bounds.Bottom + 15; ++idx; } // 'Controls' section header var controls = createLabel("Controls", karlaFamily, 2f, FontStyle.Bold, new Padding(4), new Rectangle(0, y, 0, 0)); // 'Move: Ctrl + Alt + ArrowKey' var moveLabel = createLabel("Move: ", karlaFamily, 1.5f, FontStyle.Regular, new Padding(32, 3, 3, 3), new Rectangle(controls.Bounds.Left, controls.Bounds.Bottom, 0, 0)); var moveControls = createLabel("Ctrl + Alt + ArrowKey", karlaFamily, 1.5f, FontStyle.Regular, new Padding(3), new Rectangle(110, moveLabel.Bounds.Top, 0, 0)); // 'Expand: Ctrl + Alt + Shift + ArrowKey' var expandLabel = createLabel("Expand: ", karlaFamily, 1.5f, FontStyle.Regular, new Padding(32, 3, 3, 3), new Rectangle(moveLabel.Bounds.Left, moveLabel.Bounds.Bottom, 0, 0)); var expandText = createLabel("Ctrl + Alt + Shift + ArrowKey", karlaFamily, 1.5f, FontStyle.Regular, new Padding(3), new Rectangle(110, expandLabel.Bounds.Top, 0, 0)); // 'Options' section header var optionsLabel = createLabel("Options", karlaFamily, 2f, FontStyle.Bold, new Padding(4), new Rectangle(0, expandText.Bounds.Bottom + 30, 0, 0)); var runOnStartupLabel = createLabel("Run on Startup: ", karlaFamily, 1.5f, FontStyle.Regular, new Padding(32, 0, 0, 0), new Rectangle(0, optionsLabel.Bottom, 0, 0)); var runOnStartup = new System.Windows.Forms.CheckBox(); runOnStartup.Checked = _runOnStartup; runOnStartup.Bounds = new Rectangle(runOnStartupLabel.Bounds.Right + 10, runOnStartupLabel.Bounds.Top + 5, 0, 0); runOnStartup.AutoSize = true; runOnStartup.CheckedChanged += (object sender, EventArgs e) => { // Add or remove register key var rk = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); if (runOnStartup.Checked) { rk.SetValue("fts_winsnap", Application.ExecutablePath); } else { rk.DeleteValue("fts_winsnap", false); } // Update settings _runOnStartup = runOnStartup.Checked; SaveSettings(); }; this.Controls.Add(runOnStartup); }