/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using JetBrains.Omea.GUIControls; using JetBrains.Omea.GUIControls.CommandBar; using JetBrains.Omea.OpenAPI; using JetBrains.Omea.Workspaces; using JetBrains.UI.Components.ImageListButton; using JetBrains.UI.Interop; namespace JetBrains.Omea { /// /// Implements a row for the Omea Main Frame that hosts the workspace buttons and shortcuts control. /// public class WorkspaceButtonsRow : UserControl, ICommandBarSite, IBackgroundBrushProvider { #region Data /// /// Required designer variable. /// private Container components = null; /// /// A child control that renders the workspace buttons bar. /// internal WorkspaceButtonsManager _workspaceButtonsManager; /// /// A child control that renders the shortcuts bar. /// internal ShortcutBar _barShortcut; /// /// Cached size of the workspace bar title width. /// protected int _nTitleWidth; /// /// A base color from which the other colors are produced. /// protected static Color _colorBase = Color.FromArgb( 224, 226, 235 ); /// /// Stores the visibility of the workspace buttons. /// Upon load, restored from the settings by the . /// private bool _bWorkspaceButtonsVisible = true; /// /// Stores the visibility of the shortcut bar. /// Upon load, restored from the settings by the . /// private bool _bShortcutBarVisible = true; /// /// Regulates the desired width of the toolbar, as it was adjusted by the user. /// Initially populated with the of the toolbar in . /// Persisted in settings. /// protected int _nDesiredToolbarWidth; #endregion #region Ctor/Dtor public WorkspaceButtonsRow() { // This call is required by the Windows.Forms Form Designer. InitializeComponentSelf(); // Calculate the text label size _nTitleWidth = JetLinkLabel.GetTextSize( this, Text, Font ).Width; } /// /// Clean up any resources being used. /// protected override void Dispose( bool disposing ) { if( disposing ) { if( components != null ) { components.Dispose(); } } base.Dispose( disposing ); } #endregion #region Visual Init /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponentSelf() { components = new Container(); using( new LayoutSuspender( this ) ) { // _barShortcut _barShortcut = new ShortcutBar(); _barShortcut.SetSite( this ); _barShortcut.AllowDrop = true; _barShortcut.BackColor = SystemColors.Control; _barShortcut.ColorScheme = null; _barShortcut.Name = "_shortcutBar"; _barShortcut.Size = new Size( _barShortcut.OptimalSize.Width, 27 ); _barShortcut.TabIndex = 0; _nDesiredToolbarWidth = 300; // Take 300 pixels of width by default // This Control Controls.Add( _barShortcut ); _workspaceButtonsManager = new WorkspaceButtonsManager( this ); _workspaceButtonsManager.SetSite( this ); Text = "Workspaces"; Font = new Font( "Tahoma", 8.25F, FontStyle.Bold, GraphicsUnit.Point, ((Byte)(204)) ); Height = 39; TextChanged += new EventHandler( OnTitleChanged ); FontChanged += new EventHandler( OnTitleChanged ); SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.CacheText | ControlStyles.ContainerControl | ControlStyles.ResizeRedraw | ControlStyles.Selectable | ControlStyles.UserPaint | ControlStyles.Opaque , true ); } } #endregion #region ICommandBarSite Interface Members public bool RequestMove( ICommandBar sender, Size offset ) { if( !Object.ReferenceEquals( sender, _barShortcut ) ) throw new InvalidOperationException(); _nDesiredToolbarWidth = _barShortcut.Width - offset.Width; // Calc the new desired size _nDesiredToolbarWidth = _nDesiredToolbarWidth >= 1 ? _nDesiredToolbarWidth : 1; // Constrain PerformLayout(); // Apply the new desired width to the layout Refresh(); // Request the immediate update return true; } public bool RequestSize( ICommandBar sender, Size difference ) { throw new NotImplementedException(); } public bool PerformLayout( ICommandBar sender ) { Core.UserInterfaceAP.QueueJob( "Layout the Row", new MethodInvoker( PerformLayout ) ); return true; } #endregion #region Painting /// /// Colors used in painting. /// Are filled in by invoked from the step /// just because in this case it won't happen on each painting, and the layouter is guaranteed to be called on each change /// that may potentionally affect the coloring. /// public struct Colors { /// /// Base color for the border (with which its internals is filled; analogous to the color). /// public Color BorderBase; /// /// Light color for the border (with which its highlighted parts are filled; analogous to the color). /// public Color BorderLight; /// /// Dark color for the border (with which its shadowed parts are filled; analogous to the color). /// public Color BorderDark; /// /// Color of the shadow that the border casts on the background. /// public Color BorderShadow; } private Colors _colors = new Colors(); /// /// Paints the workspace bar (background, borders & separators) according to the rectangles that were defined by the layouting procedure. /// protected override void OnPaint( PaintEventArgs e ) { using( Brush brushBackAboveBorder = GetBackgroundBrush( ClientRectangle, true ) ) using( Brush brushBackBelowBorder = GetBackgroundBrush( ClientRectangle, false ) ) { // Implement the painting foreach( Rects.Drawing drawing in _rects.Drawings ) { switch( drawing.What ) { case DrawType.BackAboveBorder: // Background above the border e.Graphics.FillRectangle( brushBackAboveBorder, drawing.Rect ); break; case DrawType.BackBelowBorder: // Background below the border e.Graphics.FillRectangle( brushBackBelowBorder, drawing.Rect ); break; case DrawType.BorderHor: // Horizontal borders DrawBorder( drawing.Rect, e.Graphics, drawing.What ); break; case DrawType.BorderVerLeft: // Vertical borders goto case DrawType.BorderHor; case DrawType.BorderVerRight: // Vertical borders goto case DrawType.BorderHor; case DrawType.CornerLeftTop: // Corners DrawBorderCorner( drawing.Rect, e.Graphics, drawing.What ); break; case DrawType.CornerLeftBottom: goto case DrawType.CornerLeftTop; case DrawType.CornerRightBottom: goto case DrawType.CornerLeftTop; case DrawType.CornerRightTop: goto case DrawType.CornerLeftTop; case DrawType.Separator: // Separators between the WSP buttons int nMiddle = drawing.Rect.Left + (drawing.Rect.Width - 2) / 2; e.Graphics.FillRectangle( SystemBrushes.ControlDark, Rectangle.FromLTRB( nMiddle, drawing.Rect.Top + 1, nMiddle + 1, drawing.Rect.Bottom - 1 ) ); e.Graphics.FillRectangle( SystemBrushes.ControlLightLight, Rectangle.FromLTRB( nMiddle + 1, drawing.Rect.Top + 1, nMiddle + 2, drawing.Rect.Bottom - 1 ) ); // TODO: remove //e.Graphics.DrawLine( SystemPens.ControlDark, new Point( nMiddle, drawing.Rect.Top + 1 ), new Point( nMiddle, drawing.Rect.Bottom - 2 ) ); //e.Graphics.DrawLine( SystemPens.ControlLightLight, new Point( nMiddle + 1, drawing.Rect.Top + 1 ), new Point( nMiddle + 1, drawing.Rect.Bottom - 2 ) ); break; case DrawType.TitleText: // Draw the title JetLinkLabel.DrawText( e.Graphics, Text, drawing.Rect, Font, SystemColors.ControlText, DrawTextFormatFlags.DT_CENTER | DrawTextFormatFlags.DT_NOPREFIX | DrawTextFormatFlags.DT_SINGLELINE | DrawTextFormatFlags.DT_VCENTER ); break; } } } } /// /// Draws a piece of the horizontal border within the bounding rectangle specified. /// /// Bounding rectangle of the border. The whole border is fit into it, including its frame. /// Type of the border to draw, the accepted values are , , and . private void DrawBorder( Rectangle rect, Graphics g, DrawType what ) { using( Brush brushLight = new SolidBrush( _colors.BorderLight ) ) using( Brush brushDark = new SolidBrush( _colors.BorderDark ) ) using( Brush brushShadow = new SolidBrush( _colors.BorderShadow ) ) using( Brush brushBase = new SolidBrush( _colors.BorderBase ) ) { switch( what ) { case DrawType.BorderHor: g.FillRectangle( brushLight, Rectangle.FromLTRB( rect.Left, rect.Top, rect.Right, rect.Top + 1 ) ); // Upper light line g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left, rect.Top + 1, rect.Right, rect.Bottom - 2 ) ); // Body g.FillRectangle( brushDark, Rectangle.FromLTRB( rect.Left, rect.Bottom - 2, rect.Right, rect.Bottom - 1 ) ); // Lower very dark line g.FillRectangle( brushShadow, Rectangle.FromLTRB( rect.Left, rect.Bottom - 1, rect.Right, rect.Bottom ) ); // Lowermost slightly dark line // TODO: remove // g.DrawLine( penLight, rect.Left, rect.Top, rect.Right, rect.Top ); // Upper light line // g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left, rect.Top + 1, rect.Right, rect.Bottom - 2 ) ); // Body // g.DrawLine( penDark, rect.Left, rect.Bottom - 2, rect.Right, rect.Bottom - 2 ); // Lower very dark line // g.DrawLine( penShadow, rect.Left, rect.Bottom - 1, rect.Right, rect.Bottom - 1 ); // Lowermost slightly dark line break; case DrawType.BorderVerLeft: g.FillRectangle( brushLight, Rectangle.FromLTRB( rect.Left, rect.Top, rect.Left + 1, rect.Bottom ) ); // Left light line g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left + 1, rect.Top, rect.Right - 2, rect.Bottom ) ); // Body g.FillRectangle( brushDark, Rectangle.FromLTRB( rect.Right - 2, rect.Top, rect.Right - 1, rect.Bottom ) ); // Right very dark line g.FillRectangle( brushShadow, Rectangle.FromLTRB( rect.Right - 1, rect.Top, rect.Right, rect.Bottom ) ); // Rightmost slightly dark line // TODO: remove // g.DrawLine( brushLight, rect.Left, rect.Top, rect.Left, rect.Bottom - 1 ); // Left light line // g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left + 1, rect.Top, rect.Right - 2, rect.Bottom ) ); // Body // g.DrawLine( brushDark, rect.Right - 2, rect.Top, rect.Right - 2, rect.Bottom - 1 ); // Right very dark line // g.DrawLine( brushShadow, rect.Right - 1, rect.Top, rect.Right - 1, rect.Bottom - 1 ); // Rightmost slightly dark line break; case DrawType.BorderVerRight: g.FillRectangle( brushLight, Rectangle.FromLTRB( rect.Left, rect.Top, rect.Left + 1, rect.Bottom ) ); // Left light line g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left + 1, rect.Top, rect.Right - 1, rect.Bottom ) ); // Body g.FillRectangle( brushShadow, Rectangle.FromLTRB( rect.Right - 1, rect.Top, rect.Right, rect.Bottom ) ); // Right dark line // TODO: remove // g.DrawLine( brushLight, rect.Left, rect.Top, rect.Left, rect.Bottom - 1 ); // Left light line // g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left + 1, rect.Top, rect.Right - 1, rect.Bottom ) ); // Body // g.DrawLine( brushShadow, rect.Right - 1, rect.Top, rect.Right - 1, rect.Bottom - 1 ); // Right dark line break; default: throw new InvalidOperationException( "Unknown border type in the border-drawing routine." ); } } } /// /// Draws a specific corner of the border. /// private void DrawBorderCorner( Rectangle rect, Graphics g, DrawType corner ) { using( Brush brushLight = new SolidBrush( _colors.BorderLight ) ) using( Brush brushDark = new SolidBrush( _colors.BorderDark ) ) using( Brush brushShadow = new SolidBrush( _colors.BorderShadow ) ) using( Brush brushBase = new SolidBrush( _colors.BorderBase ) ) { switch( corner ) { case DrawType.CornerLeftTop: // Base g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left + 1, rect.Top + 1, rect.Right, rect.Bottom ) ); // Light frame g.FillRectangle( brushLight, Rectangle.FromLTRB( rect.Left + 2, rect.Top, rect.Right, rect.Top + 1 ) ); g.FillRectangle( brushLight, new Rectangle( new Point( rect.Left + 1, rect.Top + 1 ), new Size( 1, 1 ) ) ); g.FillRectangle( brushLight, Rectangle.FromLTRB( rect.Left, rect.Top + 2, rect.Left + 1, rect.Bottom ) ); // Dark frame g.FillRectangle( brushDark, new Rectangle( new Point( rect.Right - 1, rect.Bottom - 2 ), new Size( 1, 1 ) ) ); g.FillRectangle( brushDark, new Rectangle( new Point( rect.Right - 2, rect.Bottom - 1 ), new Size( 1, 1 ) ) ); // Shadow g.FillRectangle( brushShadow, new Rectangle( new Point( rect.Right - 1, rect.Bottom - 1 ), new Size( 1, 1 ) ) ); break; case DrawType.CornerRightTop: // Base g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left, rect.Top + 1, rect.Right - 1, rect.Bottom ) ); // Light frame g.FillRectangle( brushLight, Rectangle.FromLTRB( rect.Left, rect.Top, rect.Right - 2, rect.Top + 1 ) ); g.FillRectangle( brushLight, new Rectangle( new Point( rect.Left, rect.Bottom - 1 ), new Size( 1, 1 ) ) ); // Dark frame (in color of shadow) g.FillRectangle( brushShadow, new Rectangle( new Point( rect.Right - 2, rect.Top + 1 ), new Size( 1, 1 ) ) ); g.FillRectangle( brushShadow, Rectangle.FromLTRB( rect.Right - 1, rect.Top + 2, rect.Right, rect.Bottom ) ); break; case DrawType.CornerRightBottom: // Base g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left, rect.Top, rect.Right - 2, rect.Bottom - 2 ) ); // Light frame g.FillRectangle( brushLight, new Rectangle( rect.Location, new Size( 1, 1 ) ) ); // Dark frame g.FillRectangle( brushDark, Rectangle.FromLTRB( rect.Right - 2, rect.Top, rect.Right - 1, rect.Bottom - 2 ) ); g.FillRectangle( brushDark, Rectangle.FromLTRB( rect.Left, rect.Bottom - 2, rect.Right - 2, rect.Bottom - 1 ) ); // Shadow g.FillRectangle( brushShadow, Rectangle.FromLTRB( rect.Right - 1, rect.Top, rect.Right, rect.Bottom - 3 ) ); g.FillRectangle( brushShadow, Rectangle.FromLTRB( rect.Left, rect.Bottom - 1, rect.Right - 3, rect.Bottom ) ); g.FillRectangle( brushShadow, new Rectangle( new Point( rect.Right - 2, rect.Bottom - 2 ), new Size( 1, 1 ) ) ); break; case DrawType.CornerLeftBottom: // Base g.FillRectangle( brushBase, Rectangle.FromLTRB( rect.Left + 1, rect.Top, rect.Right, rect.Bottom - 2 ) ); // Light frame g.FillRectangle( brushLight, Rectangle.FromLTRB( rect.Left, rect.Top, rect.Left + 1, rect.Bottom - 3 ) ); // Dark frame g.FillRectangle( brushDark, Rectangle.FromLTRB( rect.Right - 2, rect.Bottom - 2, rect.Right, rect.Bottom - 1 ) ); // Shadow g.FillRectangle( brushShadow, new Rectangle( new Point( rect.Right - 1, rect.Top ), new Size( 1, 1 ) ) ); g.FillRectangle( brushShadow, new Rectangle( new Point( rect.Right - 1, rect.Bottom - 1 ), new Size( 1, 1 ) ) ); break; default: throw new InvalidOperationException( "The drawing type passed in is not for a corner." ); } } } /// /// Produces the colors needed for painting and fills the structure with them. /// private void SetColors() { // If the default workspace is selected, or the workspace manager is not available yet, use the default colors for the bar IResource workspace = Core.State == CoreState.Running ? Core.WorkspaceManager.ActiveWorkspace : null; // Persist to prevent from race conditions WorkspaceUIManager ui = new WorkspaceUIManager( workspace ); _colors.BorderBase = ui.GetWorkspaceColor( WorkspaceUIManager.Colors.Base ); _colors.BorderLight = ui.GetWorkspaceColor( WorkspaceUIManager.Colors.Light ); _colors.BorderDark = ui.GetWorkspaceColor( WorkspaceUIManager.Colors.Dark ); _colors.BorderShadow = ui.GetWorkspaceColor( WorkspaceUIManager.Colors.Shadow ); // TODO: remove /* if( workspace == null ) // The default workspace is selected, or the core is not running yet { _colors.BorderBase = SystemColors.Control; _colors.BorderLight = SystemColors.ControlLightLight; _colors.BorderDark = SystemColors.ControlDarkDark; _colors.BorderShadow = SystemColors.ControlDark; } else // Use the workspace's colors { double weight = (double) ColorManagement.MaxHLS / 100; // Converts the hue from 0 … 100 to the desired range // Hue of the border color _colors.BorderHue = WorkspaceUIManager.GetWorkspaceColorHue( workspace ); _colors.BorderBase = ColorManagement.HLStoRGB( _colors.BorderHue, (ushort) (38 * weight), (ushort) (79 * weight) ); _colors.BorderLight = ColorManagement.HLStoRGB( _colors.BorderHue, (ushort) (56 * weight), (ushort) (50 * weight) ); _colors.BorderDark = ColorManagement.HLStoRGB( _colors.BorderHue, (ushort) (27 * weight), (ushort) (74 * weight) ); _colors.BorderShadow = SystemColors.ControlDark; / * _colors.BorderBase = ColorManagement.HLStoRGB( hueBorder, (ushort) (47 * weight), (ushort) (93 * weight) ); _colors.BorderLight = ColorManagement.HLStoRGB( hueBorder, (ushort) (69* weight), (ushort) (95* weight) ); _colors.BorderDark = ColorManagement.HLStoRGB( hueBorder, (ushort) (34 * weight), (ushort) (81 * weight) ); * / } */ } /// /// Creates a brush for either of the two fill types of this control. /// /// If the brush to be created is a gradient brush, this rectangle should define its application rect. /// Whether the brush requested is a brush above the border (gradient) or below it. /// The requested brush protected Brush GetBackgroundBrush( Rectangle rect, bool bAboveBorder ) { return bAboveBorder ? (Brush)new LinearGradientBrush( rect, SystemColors.ControlLightLight, SystemColors.Control, LinearGradientMode.Vertical ) : (Brush)new SolidBrush( SystemColors.Control ); } #endregion #region Layouting #region Layouting Markers internal class Rects { #region Parts /// /// Title part bound box. /// public Rectangle TitlePart; /// /// WSP part bound box. /// public Rectangle WspPart; /// /// Shortcuts part bound box. /// public Rectangle ShortcutsPart; /// /// Free space part bound box. /// public Rectangle FreeSpacePart; #endregion #region Parts — Wsp Part /// /// Box within which the workspace buttons should be placed (). /// public Rectangle WspButtonsBox; #endregion #region Operations /// /// Resets the object. /// Clears all the rects lists. /// public void Clear() { Drawings.Clear(); } #endregion /// /// A structure that describes one drawing: a rectangle and what should be drawn in it. /// internal struct Drawing { /// /// Rectangle that should be drawn out of this struct. /// public Rectangle Rect; /// /// What should be drawn in it. /// public DrawType What; /// /// Initializes the instance. /// public Drawing( DrawType what, Rectangle rc ) { Rect = rc; What = what; } } /// /// The list of drawings (rectangles + what-to-draw). /// public ArrayList Drawings = new ArrayList(); public void Draw( DrawType what, Rectangle rc ) { Drawings.Add( new Drawing( what, rc ) ); } } /// /// The layout rectangles. /// private Rects _rects = new Rects(); #endregion #region Layouting Constants /// /// Layouting Constants /// internal struct Const { /// /// Padding to the right and to the left of the title text. /// public static readonly int TitleHorPadding = 10; /// /// Border width (including its frames), in its main, straight part (the straight border along the lower side of the workspace buttons row). /// public static readonly int StraightBorderWidth = 6; /// /// Border width (including its frames) of the left vertical border. /// public static readonly int VerticalLeftBorderWidth = 5; /// /// Border width (including its frames) of the right vertical border. /// public static readonly int VerticalRightBorderWidth = 4; /// /// Width of the upper horizontal border along the upper side of the workspace buttons row, including the frames. /// public static readonly int UpperBorderWidth = 5; /// /// Padding to the right and to the left of the workspace bar. /// public static readonly int WorkspaceBarHorPadding = 10; /// /// Padding to the right and to the left of the shortcut bar. /// public static readonly int ShortcutBarHorPadding = 2; /// /// Padding above and below the shortcut bar. /// public static readonly int ShortcutBarVerPadding = 2; /// /// Padding above the upper border in its raised part (WSP bar part). /// public static readonly int UpperBorderTopPadding = 4; /// /// Spacing between the two controls if there's a vertical border between them. /// public static readonly int HorSpacingWhenBorder = 12; /// /// Width of the separator line between the controls. /// public static readonly int SeparatorWidth = 2; /// /// Horizontal spacing between the controls in case there's a vertical separator between them. /// public static readonly int HorSpacingWhenSeparator = 12; } /// /// Type of the drawing rectangle. /// internal enum DrawType { /// /// Horizontal border piece. /// BorderHor, /// /// Vertical border piece, left one (with LT and RB corners). /// BorderVerLeft, /// /// Vertical border piece, left one (with RT and LB corners). /// BorderVerRight, /// /// A place with gradiented background. /// BackAboveBorder, /// /// A lower place that should be backgrounded with a plain color. /// BackBelowBorder, /// /// A vertical separator between the controls should be drawn here. /// Separator, /// /// Left-top corner. /// CornerLeftTop, /// /// Right-top corner. /// CornerRightTop, /// /// Right-bottom corner. /// CornerRightBottom, /// /// Left-bottom corner. /// CornerLeftBottom, /// /// The title text. /// TitleText } #endregion protected override void OnLayout( LayoutEventArgs levent ) { using( new LayoutSuspender( _barShortcut ) ) { // Pick the client rectangle and use it furtherly for layouting Rectangle rcClient = ClientRectangle; // Extreme components' widths (calculate the workspace buttons sizes only if they're not turned off) Size sizeWorkspaceMax = _bWorkspaceButtonsVisible ? _workspaceButtonsManager.MaxSize : Size.Empty; Size sizeWorkspaceMin = _bWorkspaceButtonsVisible ? _workspaceButtonsManager.MinSize : Size.Empty; Size sizeShortcutsMax = _barShortcut.MaxSize; Size sizeShortcutsMin = _barShortcut.MinSize; /////////////////////// // Widths of the parts int nTitlePart; int nWspPart; int nShortcutsPart; int nFreeSpacePart; OnLayout_NegotiateParts( rcClient, sizeShortcutsMin, sizeShortcutsMax, sizeWorkspaceMin, sizeWorkspaceMax, out nTitlePart, out nWspPart, out nShortcutsPart, out nFreeSpacePart ); ////////// // Calculate the parts' rectangles as we now known their sizes _rects.TitlePart = new Rectangle( rcClient.Left, rcClient.Top, nTitlePart, rcClient.Height ); _rects.WspPart = new Rectangle( _rects.TitlePart.Right, rcClient.Top, nWspPart, rcClient.Height ); _rects.FreeSpacePart = new Rectangle( _rects.WspPart.Right, rcClient.Top, nFreeSpacePart, rcClient.Height ); _rects.ShortcutsPart = new Rectangle( _rects.FreeSpacePart.Right, rcClient.Top, nShortcutsPart, rcClient.Height ); if( _rects.ShortcutsPart.Right != rcClient.Right ) throw new InvalidOperationException( "Invalid part widths sum." ); // Reset the collections _rects.Clear(); // Layout the individual parts OnLayout_TitlePart(); OnLayout_WspPart(); OnLayout_FreeSpacePart(); OnLayout_ShortcutsPart(); } // Update the colors SetColors(); // Apply the visual changes Invalidate( true ); } /// /// A sub-function of the function. /// Negotiates the sizes of the main parts according to the user draggings and min/max size constraints, /// returns the part sizes or zeros if the corresponding part should be turned off. /// private void OnLayout_NegotiateParts( Rectangle rcClient, Size sizeShortcutsMin, Size sizeShortcutsMax, Size sizeWorkspaceMin, Size sizeWorkspaceMax, out int nTitlePart, out int nWspPart, out int nShortcutsPart, out int nFreeSpacePart ) { // Min-sizes for the parts int nShortcutsPartMin; int nWspPartMin; // Title part size is fixed, calculate it (don't show if the workspace buttons are not visible) nTitlePart = _bWorkspaceButtonsVisible ? _nTitleWidth + Const.TitleHorPadding * 2 : 0; // Shortcuts part, the constrained user-defined size if( _bShortcutBarVisible ) _barShortcut.Width = _nDesiredToolbarWidth <= sizeShortcutsMax.Width ? (_nDesiredToolbarWidth >= sizeShortcutsMin.Width ? _nDesiredToolbarWidth : sizeShortcutsMin.Width) : sizeShortcutsMax.Width; // Take the constrained desired size as a starting point nShortcutsPart = _bShortcutBarVisible ? _barShortcut.Width + Const.ShortcutBarHorPadding * 2 : 0; nShortcutsPartMin = _bShortcutBarVisible ? sizeShortcutsMin.Width + Const.ShortcutBarHorPadding * 2 : 0; // Wsp part, the rest nWspPartMin = sizeWorkspaceMin.Width; nWspPart = rcClient.Width - nTitlePart - nShortcutsPart; // Does not fit? Shrink the title & recalc if( nWspPart < nWspPartMin ) { nTitlePart = 0; nWspPart = rcClient.Width - nTitlePart - nShortcutsPart; } // Still unfit? Strip the shortcuts down (to their min size, if needed) if( nWspPart < nWspPartMin ) { // Let shortcuts part occupy all the available space left from the WSP part (do not allow to grow) nShortcutsPart = rcClient.Width - nTitlePart - nWspPartMin < nShortcutsPart ? rcClient.Width - nTitlePart - nWspPartMin : nShortcutsPart; nShortcutsPart = nShortcutsPart >= nShortcutsPartMin ? nShortcutsPart : nShortcutsPartMin; // Constrain to the min-size nWspPart = rcClient.Width - nTitlePart - nShortcutsPart; } // No? Throw the shortcuts away if( nWspPart < nWspPartMin ) { nShortcutsPart = 0; nWspPart = rcClient.Width - nTitlePart - nShortcutsPart; } // Finally, throw away everything if( nWspPart < nWspPartMin ) { nWspPart = 0; } ////////////// // Constrain the WspPart size nWspPart = nWspPart <= sizeWorkspaceMax.Width ? nWspPart : sizeWorkspaceMax.Width; // Calculate the free part size nFreeSpacePart = rcClient.Width - nTitlePart - nWspPart - nShortcutsPart; // Check for errors, set all to free space if have problems if( (nTitlePart < 0) || (nWspPart < 0) || (nShortcutsPart < 0) || (nFreeSpacePart < 0) ) { nTitlePart = nWspPart = nShortcutsPart = 0; nFreeSpacePart = rcClient.Width; } } /// /// A part of the method. /// Layouts the rects inside the title part. /// private void OnLayout_TitlePart() { Rectangle part = _rects.TitlePart; if( part.Width == 0 ) // Part turned off? return; else { Rectangle rcWithoutBorder = new Rectangle( part.Location, new Size( part.Width, part.Height - Const.StraightBorderWidth ) ); // Background _rects.Draw( DrawType.BackAboveBorder, rcWithoutBorder ); // Title text _rects.Draw( DrawType.TitleText, Rectangle.FromLTRB( part.Left + Const.TitleHorPadding, part.Top + Const.UpperBorderTopPadding + Const.UpperBorderWidth, part.Right - Const.TitleHorPadding, part.Bottom - Const.StraightBorderWidth ) ); // Border _rects.Draw( DrawType.BorderHor, new Rectangle( rcWithoutBorder.Left, rcWithoutBorder.Bottom, part.Width, Const.StraightBorderWidth ) ); } } /// /// A part of the method. /// Layouts the rects inside the free space part. /// private void OnLayout_FreeSpacePart() { Rectangle part = _rects.FreeSpacePart; // Off? if( part.Width == 0 ) return; // Background _rects.Draw( DrawType.BackAboveBorder, new Rectangle( part.Left, part.Top, part.Width, part.Height - Const.StraightBorderWidth ) ); // Lower border _rects.Draw( DrawType.BorderHor, new Rectangle( part.Left, part.Bottom - Const.StraightBorderWidth, part.Width, Const.StraightBorderWidth ) ); } /// /// A part of the method. /// Layouts the rects inside the shortcuts bar part. /// private void OnLayout_ShortcutsPart() { Rectangle part = _rects.ShortcutsPart; // Off? if( part.Width == 0 ) { _barShortcut.Visible = false; return; } Rectangle rcWithoutBorder = new Rectangle( part.Location, new Size( part.Width, part.Height - Const.StraightBorderWidth ) ); // Background _rects.Draw( DrawType.BackAboveBorder, rcWithoutBorder ); // Lower border _rects.Draw( DrawType.BorderHor, new Rectangle( rcWithoutBorder.Left, rcWithoutBorder.Bottom, rcWithoutBorder.Width, Const.StraightBorderWidth ) ); // Position the shortcuts bar Rectangle rcBar = rcWithoutBorder; rcBar.Inflate( -Const.ShortcutBarHorPadding, 0 ); _barShortcut.Bounds = new Rectangle( rcBar.Left, rcBar.Top + (rcBar.Height - _barShortcut.Height) / 2, rcBar.Width, _barShortcut.Height ); _barShortcut.Visible = true; // Adjust the height for the shortcut bar to fit its min-size Size sizeShortcutMin = _barShortcut.MinSize; if( _barShortcut.Height < sizeShortcutMin.Height ) _barShortcut.Height = sizeShortcutMin.Height; // Ensure that it fits within the row if( _barShortcut.Height + Const.ShortcutBarVerPadding * 2 + Const.StraightBorderWidth >= part.Height ) { Height += (_barShortcut.Height + Const.ShortcutBarVerPadding * 2 + Const.StraightBorderWidth) - part.Height; // Adjust the row height to fit the shortcut bar and its controls Core.UserInterfaceAP.QueueJobAt( DateTime.Now.AddMilliseconds( 500 ), "Apply layouting after growing to fit the Shortcut bar.", new MethodInvoker( PerformLayout ) ); } } /// /// A part of the method. /// Layouts the rects inside the workspace buttons part. /// private void OnLayout_WspPart() { Rectangle part = _rects.WspPart; // Output variables of the workspace buttons layouter int[] borders; int[] separators; // Off? if( (part.Width == 0) || (part.Height == 0) ) { _rects.WspButtonsBox = Rectangle.Empty; _workspaceButtonsManager.OnLayout( _rects.WspButtonsBox, out borders, out separators ); // This should hide all the controls return; } // Rectangle between the upper and lower border; the workspace buttons will go here _rects.WspButtonsBox = Rectangle.FromLTRB( part.Left, part.Top + Const.UpperBorderTopPadding + Const.UpperBorderWidth, part.Right, part.Bottom - Const.StraightBorderWidth ); // Check if there's enough space for the WSP buttons // Grow the v-size of the control, if necessary Size sizeWspBoxMin = _workspaceButtonsManager.MinSize; if( _rects.WspButtonsBox.Height < sizeWspBoxMin.Height ) { Height += sizeWspBoxMin.Height - _rects.WspButtonsBox.Height; Core.UserInterfaceAP.QueueJobAt( DateTime.Now.AddMilliseconds( 500 ), "Relayout the bar after increasing the height to fit the WSP buttons.", new MethodInvoker( PerformLayout ) ); return; } /////////////////////////////////////// // Calculate the workspace buttons layout; this will also give us a hint about the botders' placement if( !_workspaceButtonsManager.OnLayout( _rects.WspButtonsBox, out borders, out separators ) ) { // Layout has failed. Fill the part with empty stuff _rects.Draw( DrawType.BackAboveBorder, new Rectangle( part.Location, new Size( part.Width, part.Height - Const.StraightBorderWidth ) ) ); _rects.Draw( DrawType.BorderHor, new Rectangle( part.Left, part.Bottom - Const.StraightBorderWidth, part.Width, Const.StraightBorderWidth ) ); return; } // Process borders if( borders.Length != 2 ) { Trace.WriteLine( "Warning! Race condition detected: the workspace buttons layouter failed to find the active workspace button.", "[WBR]" ); if( borders.Length != 0 ) // Either two (valid case), either zero; otherwise is a fatal bug throw new InvalidOperationException( "There must be either exactly two borders, or none of them." ); // Fill the part as if all the buttons are above-border, and add no other special cues _rects.Draw( DrawType.BackAboveBorder, Rectangle.FromLTRB( part.Left, part.Top, part.Right, part.Bottom - Const.StraightBorderWidth ) ); foreach( int nPos in separators ) // All the separators _rects.Draw( DrawType.Separator, new Rectangle( nPos, _rects.WspButtonsBox.Top, Const.HorSpacingWhenSeparator, _rects.WspButtonsBox.Height ) ); // TODO: appropriate height for the separators _rects.Draw( DrawType.BorderHor, new Rectangle( part.Left, part.Bottom - Const.StraightBorderWidth, part.Width, Const.StraightBorderWidth ) ); return; } // Sub-parts for the borders: the borders array specifies left X-coordinates for the borders spacing, the borders themselves are centered within the spacing cells, extract the sub-parts that are part-high and correspond to the borders horizontally Rectangle spartLeftVBorder = new Rectangle( borders[ 0 ] + (Const.HorSpacingWhenBorder - Const.VerticalLeftBorderWidth) / 2, part.Top, Const.VerticalLeftBorderWidth, part.Height ); // Rectangle of the left vertical border Rectangle spartRightVBorder = new Rectangle( borders[ 1 ] + (Const.HorSpacingWhenBorder - Const.VerticalRightBorderWidth) / 2, part.Top, Const.VerticalRightBorderWidth, part.Height ); // Rectangle of the right vertical border ///////////////////////////////////////////////////// // Define the sub-parts around the vertical borders // (the vertical borders are not included in either of the sparts; the sparts occupy the whole height) Rectangle spartLeftmost = Rectangle.FromLTRB( part.Left, part.Top, spartLeftVBorder.Left, part.Bottom ); // Subpart to the left of the left vertical border Rectangle spartInbetween = Rectangle.FromLTRB( spartLeftVBorder.Right, part.Top, spartRightVBorder.Left, part.Bottom ); // Subpart between the two vertical borders Rectangle spartRightmost = Rectangle.FromLTRB( spartRightVBorder.Right, part.Top, part.Right, part.Bottom ); // Subpart to the right of the right vertical border //////////////////////////// // Implement the Sub-parts Rectangle corner; // Implement Spart: Leftmost _rects.Draw( DrawType.BackAboveBorder, new Rectangle( spartLeftmost.Location, new Size( spartLeftmost.Width, spartLeftmost.Height - Const.StraightBorderWidth ) ) ); // Background above the border // Separators of this part foreach( int nPos in separators ) { if( nPos < spartLeftmost.Right ) // Only those that belong to this spart _rects.Draw( DrawType.Separator, new Rectangle( nPos, _rects.WspButtonsBox.Top, Const.HorSpacingWhenSeparator, _rects.WspButtonsBox.Height ) ); } _rects.Draw( DrawType.BorderHor, new Rectangle( spartLeftmost.Left, spartLeftmost.Bottom - Const.StraightBorderWidth, spartLeftmost.Width, Const.StraightBorderWidth ) ); // Lower border // Implement Spart: Left V-Border _rects.Draw( DrawType.BorderVerLeft, Rectangle.FromLTRB( spartLeftVBorder.Left, spartLeftVBorder.Top + Const.UpperBorderTopPadding + Const.UpperBorderWidth, spartLeftVBorder.Right, spartLeftVBorder.Bottom - Const.StraightBorderWidth ) ); // Straight vertical section of the border // Upper corner corner = new Rectangle( spartLeftVBorder.Left, part.Top + Const.UpperBorderTopPadding, Const.VerticalLeftBorderWidth, Const.UpperBorderWidth ); _rects.Draw( DrawType.BackAboveBorder, corner ); _rects.Draw( DrawType.CornerLeftTop, corner ); // Lower corner corner = new Rectangle( spartLeftVBorder.Left, part.Bottom - Const.StraightBorderWidth, Const.VerticalLeftBorderWidth, Const.StraightBorderWidth ); _rects.Draw( DrawType.BackBelowBorder, corner ); _rects.Draw( DrawType.CornerRightBottom, corner ); _rects.Draw( DrawType.BackAboveBorder, new Rectangle( spartLeftVBorder.Location, new Size( spartLeftVBorder.Width, Const.UpperBorderTopPadding ) ) ); // A bit of background above the upper corner // Implement Spart: In-between _rects.Draw( DrawType.BackAboveBorder, new Rectangle( spartInbetween.Location, new Size( spartInbetween.Width, Const.UpperBorderTopPadding ) ) ); // Background above the border _rects.Draw( DrawType.BorderHor, new Rectangle( spartInbetween.Left, spartInbetween.Top + Const.UpperBorderTopPadding, spartInbetween.Width, Const.UpperBorderWidth ) ); // The upper border _rects.Draw( DrawType.BackBelowBorder, Rectangle.FromLTRB( spartInbetween.Left, spartInbetween.Top + Const.UpperBorderTopPadding + Const.UpperBorderWidth, spartInbetween.Right, spartInbetween.Bottom ) ); // Background below the upper border // Implement Spart: Right V-Border _rects.Draw( DrawType.BorderVerRight, Rectangle.FromLTRB( spartRightVBorder.Left, spartRightVBorder.Top + Const.UpperBorderTopPadding + Const.UpperBorderWidth, spartRightVBorder.Right, spartRightVBorder.Bottom - Const.StraightBorderWidth ) ); // Straight vertical section of the border // Upper corner corner = new Rectangle( spartRightVBorder.Left, part.Top + Const.UpperBorderTopPadding, Const.VerticalRightBorderWidth, Const.UpperBorderWidth ); _rects.Draw( DrawType.BackAboveBorder, corner ); _rects.Draw( DrawType.CornerRightTop, corner ); // Lower corner corner = new Rectangle( spartRightVBorder.Left, part.Bottom - Const.StraightBorderWidth, Const.VerticalRightBorderWidth, Const.StraightBorderWidth ); _rects.Draw( DrawType.BackBelowBorder, corner ); _rects.Draw( DrawType.CornerLeftBottom, corner ); _rects.Draw( DrawType.BackAboveBorder, new Rectangle( spartRightVBorder.Location, new Size( spartRightVBorder.Width, Const.UpperBorderTopPadding ) ) ); // A bit of background above the upper corner // Implement Spart: Rightmost _rects.Draw( DrawType.BackAboveBorder, new Rectangle( spartRightmost.Location, new Size( spartRightmost.Width, spartRightmost.Height - Const.StraightBorderWidth ) ) ); // Background above the border // Separators of this part foreach( int nPos in separators ) { if( nPos >= spartRightmost.Left ) // Only those that belong to this spart _rects.Draw( DrawType.Separator, new Rectangle( nPos, _rects.WspButtonsBox.Top, Const.HorSpacingWhenSeparator, _rects.WspButtonsBox.Height ) ); } _rects.Draw( DrawType.BorderHor, new Rectangle( spartRightmost.Left, spartRightmost.Bottom - Const.StraightBorderWidth, spartRightmost.Width, Const.StraightBorderWidth ) ); // Lower border } #endregion #region Attributes /// /// Gets the Workspace Bar control. /// internal WorkspaceButtonsManager WorkspaceButtonsManager { get { return _workspaceButtonsManager; } } /// /// Gets the Shortcut Bar control. /// internal ShortcutBar ShortcutBar { get { return _barShortcut; } } /// /// Gets or sets whether the workspace buttons are visible or not on the workspace buttons row. /// public bool WorkspaceButtonsVisible { get { return _bWorkspaceButtonsVisible; } set { if( _bWorkspaceButtonsVisible != value ) { _bWorkspaceButtonsVisible = value; PerformLayout(); } } } /// /// Gets or sets whether the shortcut bar is visible or not on the workspace buttons row. /// public bool ShortcutBarVisible { get { return _bShortcutBarVisible; } set { if( _bShortcutBarVisible != value ) { _bShortcutBarVisible = value; PerformLayout(); } } } #endregion #region Internal Event Handlers /// /// Either of the title properties has changed, recalc its width. /// private void OnTitleChanged( object sender, EventArgs e ) { _nTitleWidth = JetLinkLabel.GetTextSize( this, Text, Font ).Width; PerformLayout(); Invalidate(); } #endregion #region Operations /// /// Creates and returns a disposable brush for painting a background in a space either above or below the border. /// /// Control for which the brush is being requested. /// This may be needed for calculating the rect for gradient brushes. /// Background brush. public Brush GetBackgroundBrush( Control sender ) { bool bAboveBorder = true; // Check if the control represents a workspace button and if it's an active button // If yes, it's the only control that is painted below the border and should use a solid brush WorkspaceButton wb = sender as WorkspaceButton; if( (wb != null) && (wb.Active) ) bAboveBorder = false; // All the other controls should use a gradient brush, with the application rect adjusted // Create the brush appropriate return GetBackgroundBrush( sender.RectangleToClient( RectangleToScreen( ClientRectangle ) ), bAboveBorder ); } /// /// Saves or loads the resource tabs row settings. /// /// True to save, False to load. public void SerializeSettings( bool isStoring ) { string section = "MainForm"; string sDesiredToolbarWidth = "WorkspaceButtonsRow.DesiredToolbarWidth"; string sWorkspaceButtonsVisible = "WorkspaceButtonsRow.WorkspaceButtons.Visible"; string sShortcutBarVisible = "WorkspaceButtonsRow.ShortcutBar.Visible"; if( isStoring ) { Core.SettingStore.WriteInt( section, sDesiredToolbarWidth, _nDesiredToolbarWidth ); Core.SettingStore.WriteBool( section, sWorkspaceButtonsVisible, WorkspaceButtonsVisible ); Core.SettingStore.WriteBool( section, sShortcutBarVisible, ShortcutBarVisible ); } else { _nDesiredToolbarWidth = Core.SettingStore.ReadInt( section, sDesiredToolbarWidth, _nDesiredToolbarWidth ); WorkspaceButtonsVisible = Core.SettingStore.ReadBool( section, sWorkspaceButtonsVisible, WorkspaceButtonsVisible ); ShortcutBarVisible = Core.SettingStore.ReadBool( section, sShortcutBarVisible, ShortcutBarVisible ); } Visible = WorkspaceButtonsVisible; } #endregion } }