/// <summary>Internal method for rendering an individual <paramref name="tab" /> to the screen.</summary> /// <param name="graphicsContext">Graphics context to use when rendering the tab.</param> /// <param name="tab">Individual tab that we are to render.</param> /// <param name="area">Area of the screen that the tab should be rendered to.</param> /// <param name="cursor">Current position of the cursor.</param> protected virtual void Render(Graphics graphicsContext, TitleBarTab tab, Rectangle area, Point cursor) { if (_suspendRendering) { return; } // If we need to redraw the tab image if (tab.TabImage == null) { // We render the tab to an internal property so that we don't necessarily have to redraw it in every rendering pass, only if its width or // status have changed tab.TabImage = new Bitmap( area.Width, tab.Active ? _activeCenterImage.Height : _inactiveCenterImage.Height); using (Graphics tabGraphicsContext = Graphics.FromImage(tab.TabImage)) { // Draw the left, center, and right portions of the tab tabGraphicsContext.DrawImage( tab.Active ? _activeLeftSideImage : _inactiveLeftSideImage, new Rectangle( 0, 0, tab.Active ? _activeLeftSideImage .Width : _inactiveLeftSideImage .Width, tab.Active ? _activeLeftSideImage. Height : _inactiveLeftSideImage .Height), 0, 0, tab.Active ? _activeLeftSideImage.Width : _inactiveLeftSideImage.Width, tab.Active ? _activeLeftSideImage.Height : _inactiveLeftSideImage.Height, GraphicsUnit.Pixel); tabGraphicsContext.DrawImage( tab.Active ? _activeCenterImage : _inactiveCenterImage, new Rectangle( (tab.Active ? _activeLeftSideImage. Width : _inactiveLeftSideImage .Width), 0, _tabContentWidth, tab.Active ? _activeCenterImage . Height : _inactiveCenterImage . Height), 0, 0, _tabContentWidth, tab.Active ? _activeCenterImage.Height : _inactiveCenterImage.Height, GraphicsUnit.Pixel); tabGraphicsContext.DrawImage( tab.Active ? _activeRightSideImage : _inactiveRightSideImage, new Rectangle( (tab.Active ? _activeLeftSideImage .Width : _inactiveLeftSideImage .Width) + _tabContentWidth, 0, tab.Active ? _activeRightSideImage .Width : _inactiveRightSideImage .Width, tab.Active ? _activeRightSideImage .Height : _inactiveRightSideImage .Height), 0, 0, tab.Active ? _activeRightSideImage.Width : _inactiveRightSideImage.Width, tab.Active ? _activeRightSideImage.Height : _inactiveRightSideImage. Height, GraphicsUnit.Pixel); // Draw the close button if (tab.ShowCloseButton) { Image closeButtonImage = IsOverCloseButton(tab, cursor) ? _closeButtonHoverImage : _closeButtonImage; tab.CloseButtonArea = new Rectangle( area.Width - (tab.Active ? _activeRightSideImage.Width : _inactiveRightSideImage.Width) - CloseButtonMarginRight - closeButtonImage.Width, CloseButtonMarginTop, closeButtonImage.Width, closeButtonImage.Height); tabGraphicsContext.DrawImage( closeButtonImage, tab.CloseButtonArea, 0, 0, closeButtonImage.Width, closeButtonImage.Height, GraphicsUnit.Pixel); } } tab.Area = area; } // Render the tab's saved image to the screen graphicsContext.DrawImage( tab.TabImage, area, 0, 0, tab.TabImage.Width, tab.TabImage.Height, GraphicsUnit.Pixel); // Render the icon for the tab's content, if it exists and there's room for it in the tab's content area if (tab.Content.ShowIcon && _tabContentWidth > 16 + IconMarginLeft + (tab.ShowCloseButton ? CloseButtonMarginLeft + tab.CloseButtonArea.Width + CloseButtonMarginRight : 0)) { graphicsContext.DrawIcon( new Icon(tab.Content.Icon, 16, 16), new Rectangle(area.X + OverlapWidth + IconMarginLeft, IconMarginTop + area.Y, 16, 16)); } // Render the caption for the tab's content if there's room for it in the tab's content area if (_tabContentWidth > (tab.Content.ShowIcon ? 16 + IconMarginLeft + IconMarginRight : 0) + CaptionMarginLeft + CaptionMarginRight + (tab.ShowCloseButton ? CloseButtonMarginLeft + tab.CloseButtonArea.Width + CloseButtonMarginRight : 0)) { graphicsContext.DrawString( tab.Caption, SystemFonts.CaptionFont, Brushes.Black, new Rectangle( area.X + OverlapWidth + CaptionMarginLeft + (tab.Content.ShowIcon ? IconMarginLeft + 16 + IconMarginRight : 0), CaptionMarginTop + area.Y, _tabContentWidth - (tab.Content.ShowIcon ? IconMarginLeft + 16 + IconMarginRight : 0) - (tab.ShowCloseButton ? _closeButtonImage.Width + CloseButtonMarginRight + CloseButtonMarginLeft : 0), tab.TabImage.Height), new StringFormat(StringFormatFlags.NoWrap) { Trimming = StringTrimming.EllipsisCharacter }); } }
private void CloseTab(TitleBarTab clickedTab) { if (_parentForm.AutoCloseTab) { clickedTab.Content.Close(); } _parentForm.OnTabClosed( new TitleBarTabEventArgs { Tab = clickedTab, TabIndex = _parentForm.SelectedTabIndex, Action = null, WasDragging = false }); }
/// <summary> /// Called when a torn tab is dragged into the <see cref="TitleBarTabs.TabDropArea" /> of <see cref="_parentWindow" />. Places the tab in the list and /// sets <see cref="IsTabRepositioning" /> to true to simulate the user continuing to drag the tab around in the window. /// </summary> /// <param name="tab">Tab that was dragged into this window.</param> /// <param name="cursorLocation">Location of the user's cursor.</param> internal virtual void CombineTab(TitleBarTab tab, Point cursorLocation) { // Stop rendering to prevent weird stuff from happening like the wrong tab being focused _suspendRendering = true; // Find out where to insert the tab in the list int dropIndex = _parentWindow.Tabs.FindIndex(t => t.Area.Left <= cursorLocation.X && t.Area.Right >= cursorLocation.X); // Simulate the user having clicked in the middle of the tab when they started dragging it so that the tab will move correctly within the window // when the user continues to move the mouse if (_parentWindow.Tabs.Count > 0) { _tabClickOffset = _parentWindow.Tabs.First().Area.Width / 2; } else { _tabClickOffset = 0; } IsTabRepositioning = true; tab.Parent = _parentWindow; if (dropIndex == -1) { _parentWindow.Tabs.Add(tab); dropIndex = _parentWindow.Tabs.Count - 1; } else { _parentWindow.Tabs.Insert(dropIndex, tab); } // Resume rendering _suspendRendering = false; _parentWindow.SelectedTabIndex = dropIndex; _parentWindow.ResizeTabContents(); }
/// <summary>Callback that is invoked whenever anything is added or removed from <see cref="Tabs" /> so that we can trigger a redraw of the tabs.</summary> /// <param name="sender">Object for which this event was raised.</param> /// <param name="e">Arguments associated with the event.</param> private void _tabs_CollectionModified(object sender, ListModificationEventArgs e) { SetFrameSize(); if (e.Modification == ListModification.ItemAdded || e.Modification == ListModification.RangeAdded) { for (int i = 0; i < e.Count; i++) { TitleBarTab currentTab = Tabs[i + e.StartIndex]; currentTab.Content.TextChanged += Content_TextChanged; currentTab.Closing += TitleBarTabs_Closing; if (AeroPeekEnabled) { TaskbarManager.Instance.TabbedThumbnail.SetActiveTab(CreateThumbnailPreview(currentTab)); } } } if (_overlay != null) { _overlay.Render(true); } }
/// <summary> /// Creates a new thumbnail for <paramref name="tab" /> when the application is initially enabled for AeroPeek or when it is turned on sometime during /// execution. /// </summary> /// <param name="tab">Tab that we are to create the thumbnail for.</param> /// <returns>Thumbnail created for <paramref name="tab" />.</returns> protected virtual TabbedThumbnail CreateThumbnailPreview(TitleBarTab tab) { TabbedThumbnail preview = TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(tab.Content); if (preview != null) { TaskbarManager.Instance.TabbedThumbnail.RemoveThumbnailPreview(tab.Content); } preview = new TabbedThumbnail(Handle, tab.Content) { Title = tab.Content.Text, Tooltip = tab.Content.Text }; preview.SetWindowIcon((Icon)tab.Content.Icon.Clone()); preview.TabbedThumbnailActivated += preview_TabbedThumbnailActivated; preview.TabbedThumbnailClosed += preview_TabbedThumbnailClosed; preview.TabbedThumbnailBitmapRequested += preview_TabbedThumbnailBitmapRequested; preview.PeekOffset = new Vector(Padding.Left, Padding.Top - 1); TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(preview); return(preview); }
public TitleBarTabEventArgs(TabControlAction?action, TitleBarTab tab, int tabIndex, bool wasDragging) { Action = action; Tab = tab; TabIndex = tabIndex; WasDragging = wasDragging; }
private void ShowTooltipTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (!_parentForm.ShowTooltips) { return; } Point relativeCursorPosition = GetRelativeCursorPosition(Cursor.Position); TitleBarTab hoverTab = _parentForm.TabRenderer.OverTab(_parentForm.Tabs, relativeCursorPosition); if (hoverTab != null) { TitleBarTabs hoverTabForm = hoverTab.Parent; if (hoverTabForm.InvokeRequired) { hoverTabForm.Invoke(new Action(() => { ShowTooltip(hoverTabForm, hoverTab.Caption); })); } else { ShowTooltip(hoverTabForm, hoverTab.Caption); } } }
/// <summary>Calls <see cref="CreateTab" />, adds the resulting tab to the <see cref="Tabs" /> collection, and activates it.</summary> public virtual void AddNewTab() { TitleBarTab newTab = CreateTab(); Tabs.Add(newTab); ResizeTabContents(newTab); SelectedTabIndex = _tabs.Count - 1; }
protected override void Render(Graphics graphicsContext, TitleBarTab tab, int index, Rectangle area, Point cursor, Image tabLeftImage, Image tabCenterImage, Image tabRightImage) { if (!IsWindows10 && !tab.Active && index == _parentWindow.Tabs.Count - 1) { tabRightImage = Resources.ChromeInactiveRightNoDivider; } base.Render(graphicsContext, tab, index, area, cursor, tabLeftImage, tabCenterImage, tabRightImage); }
/// <summary> /// Gets the image to use for the right side of the tab. For Chrome, we pick a specific image for inactive tabs that aren't at /// the end of the list to allow for the separation between inactive tabs to be more clearly defined. /// </summary> /// <param name="tab">Tab that we are retrieving the image for.</param> /// <returns>Right-side image for <paramref name="tab"/>.</returns> protected override Image GetTabRightImage(TitleBarTab tab) { ListWithEvents <TitleBarTab> allTabs = tab.Parent.Tabs; if (tab.Active || allTabs.IndexOf(tab) == allTabs.Count - 1) { return(base.GetTabRightImage(tab)); } return(_inactiveRightSideShadowImage); }
/// <summary>Constructor; initializes the window and constructs the tab thumbnail image to use when dragging.</summary> /// <param name="tab">Tab that was torn out of its parent window.</param> /// <param name="tabRenderer">Renderer instance to use when drawing the actual tab.</param> public TornTabForm(TitleBarTab tab, BaseTabRenderer tabRenderer) { _layeredWindow = new LayeredWindow(); _initialized = false; // Set drawing styles SetStyle(ControlStyles.DoubleBuffer, true); // This should show up as a semi-transparent borderless window Opacity = 0.70; ShowInTaskbar = false; FormBorderStyle = FormBorderStyle.None; // ReSharper disable DoNotCallOverridableMethodsInConstructor BackColor = Color.Fuchsia; // ReSharper restore DoNotCallOverridableMethodsInConstructor TransparencyKey = Color.Fuchsia; AllowTransparency = true; Disposed += TornTabForm_Disposed; // Get the tab thumbnail (full size) and then draw the actual representation of the tab onto it as well Bitmap tabContents = tab.GetImage(); Bitmap contentsAndTab = new Bitmap(tabContents.Width, tabContents.Height + tabRenderer.TabHeight, tabContents.PixelFormat); Graphics tabGraphics = Graphics.FromImage(contentsAndTab); tabGraphics.DrawImage(tabContents, 0, tabRenderer.TabHeight); bool oldShowAddButton = tabRenderer.ShowAddButton; tabRenderer.ShowAddButton = false; tabRenderer.Render( new List <TitleBarTab> { tab }, tabGraphics, new Point(0, 0), new Point(0, 0), true); tabRenderer.ShowAddButton = oldShowAddButton; // Scale the thumbnail down to half size _tabThumbnail = new Bitmap(contentsAndTab.Width / 2, contentsAndTab.Height / 2, contentsAndTab.PixelFormat); Graphics thumbnailGraphics = Graphics.FromImage(_tabThumbnail); thumbnailGraphics.InterpolationMode = InterpolationMode.High; thumbnailGraphics.CompositingQuality = CompositingQuality.HighQuality; thumbnailGraphics.SmoothingMode = SmoothingMode.AntiAlias; thumbnailGraphics.DrawImage(contentsAndTab, 0, 0, _tabThumbnail.Width, _tabThumbnail.Height); Width = _tabThumbnail.Width - 1; Height = _tabThumbnail.Height - 1; _cursorOffset = new Point(tabRenderer.TabContentWidth / 4, tabRenderer.TabHeight / 4); SetWindowPosition(Cursor.Position); }
/// <summary>Checks to see if the <paramref name="cursor" /> is over the <see cref="TitleBarTab.CloseButtonArea" /> of the given <paramref name="tab" />.</summary> /// <param name="tab">The tab whose <see cref="TitleBarTab.CloseButtonArea" /> we are to check to see if it contains <paramref name="cursor" />.</param> /// <param name="cursor">Current position of the cursor.</param> /// <returns>True if the <paramref name="tab" />'s <see cref="TitleBarTab.CloseButtonArea" /> contains <paramref name="cursor" />, false otherwise.</returns> public virtual bool IsOverCloseButton(TitleBarTab tab, Point cursor) { if (!tab.ShowCloseButton || _wasTabRepositioning) { return(false); } Rectangle absoluteCloseButtonArea = new Rectangle( tab.Area.X + tab.CloseButtonArea.X, tab.Area.Y + tab.CloseButtonArea.Y, tab.CloseButtonArea.Width, tab.CloseButtonArea.Height); return(absoluteCloseButtonArea.Contains(cursor)); }
/// <summary>Resizes the <see cref="TitleBarTab.Content" /> form of the <paramref name="tab" /> to match the size of the client area for this window.</summary> /// <param name="tab">Tab whose <see cref="TitleBarTab.Content" /> form we should resize; if not specified, we default to /// <see cref="SelectedTab" />.</param> public void ResizeTabContents(TitleBarTab tab = null) { if (tab == null) { tab = SelectedTab; } if (tab != null) { tab.Content.Location = new Point(0, Padding.Top - 1); tab.Content.Size = new Size(ClientRectangle.Width, ClientRectangle.Height - Padding.Top + 1); } }
/// <summary> /// Callback for the <see cref="TabSelected" /> event. Called when a <see cref="TitleBarTab" /> gains focus. Sets the active window in Aero Peek via a /// call to <see cref="TabbedThumbnailManager.SetActiveTab(Control)" />. /// </summary> /// <param name="e">Arguments associated with the event.</param> protected void OnTabSelected(TitleBarTabEventArgs e) { if (SelectedTabIndex != -1 && _previews.ContainsKey(SelectedTab.Content) && AeroPeekEnabled) { TaskbarManager.Instance.TabbedThumbnail.SetActiveTab(SelectedTab.Content); } _previousActiveTab = SelectedTab; if (TabSelected != null) { TabSelected(this, e); } }
/// <summary> /// Event handler that is called when a tab's <see cref="TitleBarTab.Closing" /> event is fired, which removes the tab from <see cref="Tabs" /> and /// re-renders <see cref="_overlay" />. /// </summary> /// <param name="sender">Object from which this event originated (the <see cref="TitleBarTab" /> in this case).</param> /// <param name="e">Arguments associated with the event.</param> private void TitleBarTabs_Closing(object sender, CancelEventArgs e) { TitleBarTab tab = (TitleBarTab)sender; CloseTab(tab); if (!tab.Content.IsDisposed && AeroPeekEnabled) { TaskbarManager.Instance.TabbedThumbnail.RemoveThumbnailPreview(tab.Content); } if (_overlay != null) { _overlay.Render(true); } }
private void StartTooltipTimer() { if (!_parentForm.ShowTooltips) { return; } Point relativeCursorPosition = GetRelativeCursorPosition(Cursor.Position); TitleBarTab hoverTab = _parentForm.TabRenderer.OverTab(_parentForm.Tabs, relativeCursorPosition); if (hoverTab != null) { showTooltipTimer.Interval = hoverTab.Parent.Tooltip.AutomaticDelay; showTooltipTimer.Start(); } }
public TornTabForm(TitleBarTab tab, BaseTabRenderer tabRenderer) { LayeredWindow = new LayeredWindow(); Initialized = false; SetStyle(ControlStyles.DoubleBuffer, true); Opacity = 0.70; ShowInTaskbar = false; FormBorderStyle = FormBorderStyle.None; BackColor = Color.Fuchsia; TransparencyKey = Color.Fuchsia; AllowTransparency = true; Disposed += OnDisposed; Bitmap tabContents = tab.GetThumbnail(); Bitmap contentsAndTab = new Bitmap(tabContents.Width, tabContents.Height + tabRenderer.TabHeight, tabContents.PixelFormat); Graphics tabGraphics = Graphics.FromImage(contentsAndTab); tabGraphics.DrawImage(tabContents, 0, tabRenderer.TabHeight); bool oldShowAddButton = tabRenderer.ShowAddButton; tabRenderer.ShowAddButton = false; tabRenderer.Render(new List <TitleBarTab> { tab }, tabGraphics, new Point(0, 0), new Point(0, 0), true); tabRenderer.ShowAddButton = oldShowAddButton; TabThumbnail = new Bitmap(contentsAndTab.Width / 2, contentsAndTab.Height / 2, contentsAndTab.PixelFormat); Graphics thumbnailGraphics = Graphics.FromImage(TabThumbnail); thumbnailGraphics.InterpolationMode = InterpolationMode.High; thumbnailGraphics.CompositingQuality = CompositingQuality.HighQuality; thumbnailGraphics.SmoothingMode = SmoothingMode.AntiAlias; thumbnailGraphics.DrawImage(contentsAndTab, 0, 0, TabThumbnail.Width, TabThumbnail.Height); Width = TabThumbnail.Width - 1; Height = TabThumbnail.Height - 1; CursorOffset = new Point(tabRenderer.TabContentWidth / 4, tabRenderer.TabHeight / 4); SetWindowPosition(Cursor.Position); }
/// <summary>Generate a new thumbnail image for <paramref name="tab" />.</summary> /// <param name="tab">Tab that we need to generate a thumbnail for.</param> protected void UpdateTabThumbnail(TitleBarTab tab) { TabbedThumbnail preview = TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(tab.Content); if (preview == null) { return; } Bitmap bitmap = TabbedThumbnailScreenCapture.GrabWindowBitmap(tab.Content.Handle, tab.Content.Size); preview.SetImage(bitmap); // If we already had a preview image for the tab, dispose of it if (_previews.ContainsKey(tab.Content) && _previews[tab.Content] != null) { _previews[tab.Content].Dispose(); } _previews[tab.Content] = bitmap; }
/// <summary> /// When a child tab updates its <see cref="Form.Icon"/> property, it should call this method to update the icon in the AeroPeek preview. /// </summary> /// <param name="tab">Tab whose icon was updated.</param> /// <param name="icon">The new icon to use. If this is left as null, we use <see cref="Form.Icon"/> on <paramref name="tab"/>.</param> public virtual void UpdateThumbnailPreviewIcon(TitleBarTab tab, Icon icon = null) { if (!AeroPeekEnabled) { return; } TabbedThumbnail preview = TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(tab.Content); if (preview == null) { return; } if (icon == null) { icon = tab.Content.Icon; } preview.SetWindowIcon((Icon)icon.Clone()); }
/// <summary>Calls <see cref="CreateTab" />, adds the resulting tab to the <see cref="Tabs" /> collection, and activates it.</summary> public virtual void AddNewTab() { TitleBarTab newTab = null; if (AutoCreateTab) { newTab = CreateTab(); Tabs.Add(newTab); ResizeTabContents(newTab); SelectedTabIndex = _tabs.Count - 1; } var titleBarTabEventArgs = new TitleBarTabEventArgs { Tab = newTab, TabIndex = SelectedTabIndex, Action = TabControlAction.Selected, WasDragging = false }; TabCreated?.Invoke(this, titleBarTabEventArgs); }
/// <summary>Removes <paramref name="closingTab" /> from <see cref="Tabs" /> and selects the next applicable tab in the list.</summary> /// <param name="closingTab">Tab that is being closed.</param> protected virtual void CloseTab(TitleBarTab closingTab) { int removeIndex = Tabs.IndexOf(closingTab); int selectedTabIndex = SelectedTabIndex; Tabs.Remove(closingTab); if (selectedTabIndex > removeIndex) { SelectedTabIndex = selectedTabIndex - 1; } else if (selectedTabIndex == removeIndex) { SelectedTabIndex = Math.Min(selectedTabIndex, Tabs.Count - 1); } else { SelectedTabIndex = selectedTabIndex; } if (_previews.ContainsKey(closingTab.Content)) { _previews[closingTab.Content].Dispose(); _previews.Remove(closingTab.Content); } if (_previousActiveTab != null && closingTab.Content == _previousActiveTab.Content) { _previousActiveTab = null; } if (Tabs.Count == 0 && ExitOnLastTabClose) { Close(); } }
/// <summary> /// Called from the <see cref="_parentWindow" /> to determine which, if any, of the <paramref name="tabs" /> the <paramref name="cursor" /> is /// over. /// </summary> /// <param name="tabs">The list of tabs that we should check.</param> /// <param name="cursor">The relative position of the cursor within the window.</param> /// <returns>The tab within <paramref name="tabs" /> that the <paramref name="cursor" /> is over; if none, then null is returned.</returns> public virtual TitleBarTab OverTab(IEnumerable <TitleBarTab> tabs, Point cursor) { TitleBarTab overTab = null; foreach (TitleBarTab tab in tabs.Where(tab => tab.TabImage != null)) { // We have to loop through each of the tabs in turn and check their status; if the tabs overlap, then their areas overlap as well, which means // that we may find see that the cursor is over an inactive tab, but we need to check the active tabs as well, since they may overlap their // areas and take precedence. if (tab.Active && IsOverTab(tab, cursor)) { overTab = tab; break; } if (IsOverTab(tab, cursor)) { overTab = tab; } } return(overTab); }
/// <summary>Overrides the message pump for the window so that we can respond to click events on the tabs themselves.</summary> /// <param name="m">Message received by the pump.</param> protected override void WndProc(ref Message m) { switch ((WM)m.Msg) { case WM.WM_NCLBUTTONDOWN: case WM.WM_LBUTTONDOWN: Point relativeCursorPosition = GetRelativeCursorPosition(Cursor.Position); // If we were over a tab, set the capture state for the window so that we'll actually receive a WM_LBUTTONUP message if (_parentForm.TabRenderer.OverTab(_parentForm.Tabs, relativeCursorPosition) == null && !_parentForm.TabRenderer.IsOverAddButton(relativeCursorPosition)) { _parentForm.ForwardMessage(ref m); } else { // When the user clicks a mouse button, save the tab that the user was over so we can respond properly when the mouse button is released TitleBarTab clickedTab = _parentForm.TabRenderer.OverTab(_parentForm.Tabs, relativeCursorPosition); if (clickedTab != null) { // If the user clicked the close button, remove the tab from the list if (!_parentForm.TabRenderer.IsOverCloseButton(clickedTab, relativeCursorPosition)) { _parentForm.ResizeTabContents(clickedTab); _parentForm.SelectedTabIndex = _parentForm.Tabs.IndexOf(clickedTab); Render(); } OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, Cursor.Position.X, Cursor.Position.Y, 0)); } _parentForm.Activate(); } break; case WM.WM_LBUTTONDBLCLK: _parentForm.ForwardMessage(ref m); break; // We always return HTCAPTION for the hit test message so that the underlying window doesn't have its focus removed case WM.WM_NCHITTEST: m.Result = new IntPtr((int)HT.HTCAPTION); break; case WM.WM_LBUTTONUP: case WM.WM_NCLBUTTONUP: case WM.WM_MBUTTONUP: case WM.WM_NCMBUTTONUP: Point relativeCursorPosition2 = GetRelativeCursorPosition(Cursor.Position); if (_parentForm.TabRenderer.OverTab(_parentForm.Tabs, relativeCursorPosition2) == null && !_parentForm.TabRenderer.IsOverAddButton(relativeCursorPosition2)) { _parentForm.ForwardMessage(ref m); } else { // When the user clicks a mouse button, save the tab that the user was over so we can respond properly when the mouse button is released TitleBarTab clickedTab = _parentForm.TabRenderer.OverTab(_parentForm.Tabs, relativeCursorPosition2); if (clickedTab != null) { // If the user clicks the middle button/scroll wheel over a tab, close it if ((WM)m.Msg == WM.WM_MBUTTONUP || (WM)m.Msg == WM.WM_NCMBUTTONUP) { clickedTab.Content.Close(); Render(); } else { // If the user clicked the close button, remove the tab from the list if (_parentForm.TabRenderer.IsOverCloseButton(clickedTab, relativeCursorPosition2)) { clickedTab.Content.Close(); Render(); } else { _parentForm.OnTabClicked( new TitleBarTabEventArgs { Tab = clickedTab, TabIndex = _parentForm.SelectedTabIndex, Action = TabControlAction.Selected, WasDragging = _wasDragging }); } } } // Otherwise, if the user clicked the add button, call CreateTab to add a new tab to the list and select it else if (_parentForm.TabRenderer.IsOverAddButton(relativeCursorPosition2)) { _parentForm.AddNewTab(); } if ((WM)m.Msg == WM.WM_LBUTTONUP || (WM)m.Msg == WM.WM_NCLBUTTONUP) { OnMouseUp(new MouseEventArgs(MouseButtons.Left, 1, Cursor.Position.X, Cursor.Position.Y, 0)); } } break; default: base.WndProc(ref m); break; } }
public TitleBarTabCancelEventArgs(TabControlAction action, TitleBarTab tab, int tabIndex) { Action = action; Tab = tab; TabIndex = tabIndex; }
/// <summary> /// Creates a new tab to hold <paramref name="form"/>. /// </summary> /// <param name="form">Form instance that we are to open in a new tab.</param> protected void ShowInEmptyTab(Form form) { // If we're opening the form from an unconnected ConnectionWindow, just replace its content with the new form if (SelectedTab != null && SelectedTab.Content is ConnectionWindow && !(SelectedTab.Content as ConnectionWindow).IsConnected) { Form oldWindow = SelectedTab.Content; SelectedTab.Content = form; ResizeTabContents(); oldWindow.Close(); } // Otherwise, create a new tab associated with the form else { TitleBarTab newTab = new TitleBarTab(this) { Content = form }; Tabs.Add(newTab); ResizeTabContents(newTab); SelectedTabIndex = _tabs.Count - 1; } form.Show(); if (_overlay != null) _overlay.Render(true); }
/// <summary>Internal method for rendering an individual <paramref name="tab" /> to the screen.</summary> /// <param name="graphicsContext">Graphics context to use when rendering the tab.</param> /// <param name="tab">Individual tab that we are to render.</param> /// <param name="area">Area of the screen that the tab should be rendered to.</param> /// <param name="cursor">Current position of the cursor.</param> /// <param name="tabLeftImage">Image to use for the left side of the tab.</param> /// <param name="tabCenterImage">Image to use for the center of the tab.</param> /// <param name="tabRightImage">Image to use for the right side of the tab.</param> protected virtual void Render(Graphics graphicsContext, TitleBarTab tab, Rectangle area, Point cursor, Image tabLeftImage, Image tabCenterImage, Image tabRightImage) { if (_suspendRendering) { return; } // If we need to redraw the tab image if (tab.TabImage == null) { // We render the tab to an internal property so that we don't necessarily have to redraw it in every rendering pass, only if its width or // status have changed tab.TabImage = new Bitmap(area.Width <= 0 ? 1 : area.Width, tabCenterImage.Height <= 0 ? 1 : tabCenterImage.Height); using (Graphics tabGraphicsContext = Graphics.FromImage(tab.TabImage)) { // Draw the left, center, and right portions of the tab tabGraphicsContext.DrawImage( tabLeftImage, new Rectangle(0, 0, tabLeftImage.Width, tabLeftImage.Height), 0, 0, tabLeftImage.Width, tabLeftImage.Height, GraphicsUnit.Pixel); tabGraphicsContext.DrawImage( tabCenterImage, new Rectangle(tabLeftImage.Width, 0, _tabContentWidth, tabCenterImage.Height), 0, 0, _tabContentWidth, tabCenterImage.Height, GraphicsUnit.Pixel); tabGraphicsContext.DrawImage( tabRightImage, new Rectangle(tabLeftImage.Width + _tabContentWidth, 0, tabRightImage.Width, tabRightImage.Height), 0, 0, tabRightImage.Width, tabRightImage.Height, GraphicsUnit.Pixel); // Draw the close button if (tab.ShowCloseButton) { Image closeButtonImage = IsOverCloseButton(tab, cursor) ? _closeButtonHoverImage : _closeButtonImage; tab.CloseButtonArea = new Rectangle( area.Width - tabRightImage.Width - CloseButtonMarginRight - closeButtonImage.Width, CloseButtonMarginTop, closeButtonImage.Width, closeButtonImage.Height); tabGraphicsContext.DrawImage( closeButtonImage, tab.CloseButtonArea, 0, 0, closeButtonImage.Width, closeButtonImage.Height, GraphicsUnit.Pixel); } } tab.Area = area; } // Render the tab's saved image to the screen graphicsContext.DrawImage( tab.TabImage, area, 0, 0, tab.TabImage.Width, tab.TabImage.Height, GraphicsUnit.Pixel); // Render the icon for the tab's content, if it exists and there's room for it in the tab's content area if (tab.Content.ShowIcon && _tabContentWidth > 16 + IconMarginLeft + (tab.ShowCloseButton ? CloseButtonMarginLeft + tab.CloseButtonArea.Width + CloseButtonMarginRight : 0)) { graphicsContext.DrawIcon( new Icon(tab.Content.Icon, 16, 16), new Rectangle(area.X + OverlapWidth + IconMarginLeft, IconMarginTop + area.Y, 16, 16)); } // Render the caption for the tab's content if there's room for it in the tab's content area if (_tabContentWidth > (tab.Content.ShowIcon ? 16 + IconMarginLeft + IconMarginRight : 0) + CaptionMarginLeft + CaptionMarginRight + (tab.ShowCloseButton ? CloseButtonMarginLeft + tab.CloseButtonArea.Width + CloseButtonMarginRight : 0)) { graphicsContext.DrawString( tab.Caption, CaptionFont, new SolidBrush(ForeColor), new Rectangle( area.X + OverlapWidth + CaptionMarginLeft + (tab.Content.ShowIcon ? IconMarginLeft + 16 + IconMarginRight : 0), CaptionMarginTop + area.Y, _tabContentWidth - (tab.Content.ShowIcon ? IconMarginLeft + 16 + IconMarginRight : 0) - (tab.ShowCloseButton ? _closeButtonImage.Width + CloseButtonMarginRight + CloseButtonMarginLeft : 0), tab.TabImage.Height), new StringFormat(StringFormatFlags.NoWrap) { Trimming = StringTrimming.EllipsisCharacter }); } }
/// <summary> /// Handler method that's called when the user clicks the "Edit" menu item in the context menu that appears when the user right-clicks on a bookmark /// in the list view. Opens the options window for the connection's protocol type. /// </summary> /// <param name="sender">Object from which this event originated.</param> /// <param name="e">Arguments associated with this event.</param> private void _editBookmarkMenuItem_Click(object sender, EventArgs e) { ListViewItem selectedItem = _bookmarksListView.SelectedItems[0]; TitleBarTab optionsTab = new TitleBarTab(ParentTabs) { Content = new OptionsWindow(ParentTabs) { OptionsForms = new List<Form> { ConnectionFactory.CreateOptionsForm(_listViewConnections[selectedItem]) }, Text = "Options for " + _listViewConnections[selectedItem].DisplayName } }; // When the options form is closed, update the second column in the list view with the updated host for the connection optionsTab.Content.FormClosed += (o, args) => selectedItem.SubItems[1].Text = _listViewConnections[selectedItem].Host; ParentTabs.Tabs.Add(optionsTab); ParentTabs.ResizeTabContents(optionsTab); ParentTabs.SelectedTab = optionsTab; }
/// <summary> /// Opens a new tab for <paramref name="connection"/>. /// </summary> /// <param name="connection">Connection that we are to open a new tab for.</param> /// <param name="focusNewTab">Flag indicating whether we should focus on the new tab.</param> /// <returns>Tab that was created for <paramref name="connection"/>.</returns> public TitleBarTab Connect(IConnection connection, bool focusNewTab) { ConnectionWindow connectionWindow = new ConnectionWindow(connection); TitleBarTab newTab = new TitleBarTab(this) { Content = connectionWindow }; Tabs.Insert(SelectedTabIndex + 1, newTab); ResizeTabContents(newTab); if (focusNewTab) { SelectedTab = newTab; _previouslyClickedTab = newTab; } connectionWindow.Connect(); return newTab; }
/// <summary> /// Handler method that's called when the user clicks on the "Properties..." menu item in the context menu that appears when the user right-clicks on /// a history entry; opens up the options for the connection in a new tab. /// </summary> /// <param name="sender">Object from which this event originated.</param> /// <param name="e">Argument associated with this event.</param> private void propertiesMenuItem_Click(object sender, EventArgs e) { // Get the options form for the connection type and open it in a new tab Form optionsWindow = ConnectionFactory.CreateOptionsForm(_connections[_historyListView.SelectedItems[0]].Connection); TitleBarTab optionsTab = new TitleBarTab(_applicationForm) { Content = optionsWindow }; _applicationForm.Tabs.Add(optionsTab); _applicationForm.SelectedTab = optionsTab; }
/// <summary> /// Handler method that's called when a tab is clicked on. This is different from the <see cref="TitleBarTabs.TabSelected"/> event handler in that /// this is called even if the tab is currently active. This is used to show the toolbar for <see cref="ConnectionWindow"/> instances that /// automatically hide their toolbars when the connection's UI is focused on. /// </summary> /// <param name="sender">Object from which this event originated.</param> /// <param name="e">Arguments associated with this event.</param> private void MainForm_TabClicked(object sender, TitleBarTabEventArgs e) { // Only show the toolbar if the user clicked on an already-selected tab if (e.Tab.Content is ConnectionWindow && e.Tab == _previouslyClickedTab && !e.WasDragging) (e.Tab.Content as ConnectionWindow).ShowToolbar(); _previouslyClickedTab = e.Tab; }
/// <summary> /// Gets the image to use for the center of the <paramref name="tab"/>. /// </summary> /// <param name="tab">Tab that we are retrieving the image for.</param> /// <returns>The image for the center of <paramref name="tab"/>.</returns> protected virtual Image GetTabCenterImage(TitleBarTab tab) { return(tab.Active ? _activeCenterImage : _inactiveCenterImage); }
/// <summary> /// Consumer method that processes mouse events in <see cref="MouseEvents" /> that are recorded by <see cref="MouseHookCallback" /> /// </summary> protected void InterpretMouseEvents() { foreach (MouseEvent mouseEvent in MouseEvents.GetConsumingEnumerable()) { int nCode = mouseEvent.nCode; IntPtr wParam = mouseEvent.wParam; MSLLHOOKSTRUCT?hookStruct = mouseEvent.MouseData; if (nCode >= 0 && (int)WM.WM_MOUSEMOVE == (int)wParam) { Point cursorPosition = new Point(hookStruct.Value.pt.x, hookStruct.Value.pt.y); bool reRender = false; if (s_tornTab != null && DropAreas != null) { for (int i = 0; i < DropAreas.Length; i++) { if (DropAreas[i].Item2.Contains(cursorPosition)) // If the cursor is within the drop area, combine the tab for the window that belongs to that drop area { TitleBarTab tabToCombine = null; lock (s_tornTabLock) { if (s_tornTab != null) { tabToCombine = s_tornTab; s_tornTab = null; } } if (tabToCombine != null) { int i1 = i; Invoke(new Action(() => { DropAreas[i1].Item1.TabRenderer.CombineTab(tabToCombine, cursorPosition); tabToCombine = null; s_tornTabForm.Close(); s_tornTabForm = null; if (ParentForm.Tabs.Count == 0) { ParentForm.Close(); } })); } } } } else if (!ParentForm.TabRenderer.IsTabRepositioning) { // If we were over a close button previously, check to see if the cursor is still over that tab's close button; if not, re-render if (IsOverCLoseButtonForTab != -1 && (IsOverCLoseButtonForTab >= ParentForm.Tabs.Count || !ParentForm.TabRenderer.IsOverCloseButton(ParentForm.Tabs[IsOverCLoseButtonForTab], GetRelativeCursorPosition(cursorPosition)))) { reRender = true; IsOverCLoseButtonForTab = -1; } else // Otherwise, see if any tabs' close button is being hovered over { for (int i = 0; i < ParentForm.Tabs.Count; i++) { if (ParentForm.TabRenderer.IsOverCloseButton(ParentForm.Tabs[i], GetRelativeCursorPosition(cursorPosition))) { IsOverCLoseButtonForTab = i; reRender = true; break; } } } } else { Invoke(new Action(() => { s_wasDragging = true; Rectangle dragArea = TabDropArea; dragArea.Inflate(ParentForm.TabRenderer.TabTearDragDistance, ParentForm.TabRenderer.TabTearDragDistance); if (!dragArea.Contains(cursorPosition) && s_tornTab == null) { lock (s_tornTabLock) { if (s_tornTab == null) { ParentForm.TabRenderer.IsTabRepositioning = false; s_tornTab = ParentForm.SelectedTab; s_tornTab.ClearEventSubscriptions(); s_tornTabForm = new TornTabForm(s_tornTab, ParentForm.TabRenderer); } } if (s_tornTab != null) { ParentForm.SelectedTabIndex = ParentForm.SelectedTabIndex == ParentForm.Tabs.Count - 1 ? ParentForm.SelectedTabIndex - 1 : ParentForm.SelectedTabIndex + 1; ParentForm.Tabs.Remove(s_tornTab); if (ParentForm.Tabs.Count == 0) { ParentForm.Hide(); } s_tornTabForm.Show(); DropAreas = (from window in ParentForm.ApplicationContext.OpenWindows.Where(w => w.Tabs.Count > 0) select new Tuple <TitleBarTabs, Rectangle>(window, window.TabDropArea)).ToArray(); } } })); } Invoke(new Action(() => OnMouseMove(new MouseEventArgs(MouseButtons.None, 0, cursorPosition.X, cursorPosition.Y, 0)))); if (ParentForm.TabRenderer.IsTabRepositioning) { reRender = true; } if (reRender) { Invoke(new Action(() => Render(cursorPosition, true))); } } else if (nCode >= 0 && (int)WM.WM_LBUTTONDOWN == (int)wParam) { s_wasDragging = false; } else if (nCode >= 0 && (int)WM.WM_LBUTTONUP == (int)wParam) { if (s_tornTab != null) { TitleBarTab tabToRelease = null; lock (s_tornTabLock) { if (s_tornTab != null) { tabToRelease = s_tornTab; s_tornTab = null; } } if (tabToRelease != null) { Invoke(new Action(() => { TitleBarTabs newWindow = (TitleBarTabs)Activator.CreateInstance(ParentForm.GetType()); if (newWindow.WindowState == FormWindowState.Maximized) { Screen screen = Screen.AllScreens.First(s => s.WorkingArea.Contains(Cursor.Position)); newWindow.StartPosition = FormStartPosition.Manual; newWindow.WindowState = FormWindowState.Normal; newWindow.Left = screen.WorkingArea.Left; newWindow.Top = screen.WorkingArea.Top; newWindow.Width = screen.WorkingArea.Width; newWindow.Height = screen.WorkingArea.Height; } else { newWindow.Left = Cursor.Position.X; newWindow.Top = Cursor.Position.Y; } tabToRelease.Parent = newWindow; ParentForm.ApplicationContext.OpenWindow(newWindow); newWindow.Show(); newWindow.Tabs.Add(tabToRelease); newWindow.SelectedTabIndex = 0; newWindow.ResizeTabContents(); s_tornTabForm.Close(); s_tornTabForm = null; if (ParentForm.Tabs.Count == 0) { ParentForm.Close(); } })); } } Invoke(new Action(() => OnMouseUp(new MouseEventArgs(MouseButtons.Left, 1, Cursor.Position.X, Cursor.Position.Y, 0)))); } } }
/// <summary>Consumer method that processes mouse events in <see cref="_mouseEvents" /> that are recorded by <see cref="MouseHookCallback" />.</summary> protected void InterpretMouseEvents() { foreach (MouseEvent mouseEvent in _mouseEvents.GetConsumingEnumerable()) { int nCode = mouseEvent.nCode; IntPtr wParam = mouseEvent.wParam; MSLLHOOKSTRUCT?hookStruct = mouseEvent.MouseData; if (nCode >= 0 && (int)WM.WM_MOUSEMOVE == (int)wParam) { HideTooltip(); // ReSharper disable PossibleInvalidOperationException Point cursorPosition = new Point(hookStruct.Value.pt.x, hookStruct.Value.pt.y); // ReSharper restore PossibleInvalidOperationException bool reRender = false; if (_tornTab != null && _dropAreas != null) { // ReSharper disable ForCanBeConvertedToForeach for (int i = 0; i < _dropAreas.Length; i++) // ReSharper restore ForCanBeConvertedToForeach { // If the cursor is within the drop area, combine the tab for the window that belongs to that drop area if (_dropAreas[i].Item2.Contains(cursorPosition)) { TitleBarTab tabToCombine = null; lock (_tornTabLock) { if (_tornTab != null) { tabToCombine = _tornTab; _tornTab = null; } } if (tabToCombine != null) { int i1 = i; // In all cases where we need to affect the UI, we call Invoke so that those changes are made on the main UI thread since // we are on a separate processing thread in this case Invoke( new Action( () => { _dropAreas[i1].Item1.TabRenderer.CombineTab(tabToCombine, cursorPosition); tabToCombine = null; _tornTabForm.Close(); _tornTabForm = null; if (_parentForm.Tabs.Count == 0) { _parentForm.Close(); } })); } } } } else if (!_parentForm.TabRenderer.IsTabRepositioning) { StartTooltipTimer(); // If we were over a close button previously, check to see if the cursor is still over that tab's // close button; if not, re-render if (_isOverCloseButtonForTab != -1 && (_isOverCloseButtonForTab >= _parentForm.Tabs.Count || !_parentForm.TabRenderer.IsOverCloseButton(_parentForm.Tabs[_isOverCloseButtonForTab], GetRelativeCursorPosition(cursorPosition)))) { reRender = true; _isOverCloseButtonForTab = -1; } // Otherwise, see if any tabs' close button is being hovered over else { // ReSharper disable ForCanBeConvertedToForeach for (int i = 0; i < _parentForm.Tabs.Count; i++) // ReSharper restore ForCanBeConvertedToForeach { if (_parentForm.TabRenderer.IsOverCloseButton(_parentForm.Tabs[i], GetRelativeCursorPosition(cursorPosition))) { _isOverCloseButtonForTab = i; reRender = true; break; } } } } else { Invoke( new Action( () => { _wasDragging = true; // When determining if a tab has been torn from the window while dragging, we take the drop area for this window and inflate it by the // TabTearDragDistance setting Rectangle dragArea = TabDropArea; dragArea.Inflate(_parentForm.TabRenderer.TabTearDragDistance, _parentForm.TabRenderer.TabTearDragDistance); // If the cursor is outside the tear area, tear it away from the current window if (!dragArea.Contains(cursorPosition) && _tornTab == null) { lock (_tornTabLock) { if (_tornTab == null) { _parentForm.TabRenderer.IsTabRepositioning = false; // Clear the event handler subscriptions from the tab and then create a thumbnail representation of it to use when dragging _tornTab = _parentForm.SelectedTab; _tornTab.ClearSubscriptions(); _tornTabForm = new TornTabForm(_tornTab, _parentForm.TabRenderer); } } if (_tornTab != null) { _parentForm.SelectedTabIndex = (_parentForm.SelectedTabIndex == _parentForm.Tabs.Count - 1 ? _parentForm.SelectedTabIndex - 1 : _parentForm.SelectedTabIndex + 1); _parentForm.Tabs.Remove(_tornTab); // If this tab was the only tab in the window, hide the parent window if (_parentForm.Tabs.Count == 0) { _parentForm.Hide(); } _tornTabForm.Show(); _dropAreas = (from window in _parentForm.ApplicationContext.OpenWindows.Where(w => w.Tabs.Count > 0) select new Tuple <TitleBarTabs, Rectangle>(window, window.TabDropArea)).ToArray(); } } })); } Invoke(new Action(() => OnMouseMove(new MouseEventArgs(MouseButtons.None, 0, cursorPosition.X, cursorPosition.Y, 0)))); if (_parentForm.TabRenderer.IsTabRepositioning) { reRender = true; } if (reRender) { Invoke(new Action(() => Render(cursorPosition, true))); } } else if (nCode >= 0 && (int)WM.WM_LBUTTONDOWN == (int)wParam) { _wasDragging = false; } else if (nCode >= 0 && (int)WM.WM_LBUTTONUP == (int)wParam) { // If we released the mouse button while we were dragging a torn tab, put that tab into a new window if (_tornTab != null) { TitleBarTab tabToRelease = null; lock (_tornTabLock) { if (_tornTab != null) { tabToRelease = _tornTab; _tornTab = null; } } if (tabToRelease != null) { Invoke( new Action( () => { TitleBarTabs newWindow = (TitleBarTabs)Activator.CreateInstance(_parentForm.GetType()); // Set the initial window position and state properly if (newWindow.WindowState == FormWindowState.Maximized) { Screen screen = Screen.AllScreens.First(s => s.WorkingArea.Contains(Cursor.Position)); newWindow.StartPosition = FormStartPosition.Manual; newWindow.WindowState = FormWindowState.Normal; newWindow.Left = screen.WorkingArea.Left; newWindow.Top = screen.WorkingArea.Top; newWindow.Width = screen.WorkingArea.Width; newWindow.Height = screen.WorkingArea.Height; } else { newWindow.Left = Cursor.Position.X; newWindow.Top = Cursor.Position.Y; } tabToRelease.Parent = newWindow; _parentForm.ApplicationContext.OpenWindow(newWindow); newWindow.Show(); newWindow.Tabs.Add(tabToRelease); newWindow.SelectedTabIndex = 0; newWindow.ResizeTabContents(); _tornTabForm.Close(); _tornTabForm = null; if (_parentForm.Tabs.Count == 0) { _parentForm.Close(); } })); } } Invoke(new Action(() => OnMouseUp(new MouseEventArgs(MouseButtons.Left, 1, Cursor.Position.X, Cursor.Position.Y, 0)))); } } }
/// <summary>Renders the list of <paramref name="tabs" /> to the screen using the given <paramref name="graphicsContext" />.</summary> /// <param name="tabs">List of tabs that we are to render.</param> /// <param name="graphicsContext">Graphics context that we should use while rendering.</param> /// <param name="cursor">Current location of the cursor on the screen.</param> /// <param name="forceRedraw">Flag indicating whether or not the redraw should be forced.</param> /// <param name="offset">Offset within <paramref name="graphicsContext" /> that the tabs should be rendered.</param> public virtual void Render(List <TitleBarTab> tabs, Graphics graphicsContext, Point offset, Point cursor, bool forceRedraw = false) { if (_suspendRendering) { return; } if (tabs == null || tabs.Count == 0) { return; } Point screenCoordinates = _parentWindow.PointToScreen(_parentWindow.ClientRectangle.Location); // Calculate the maximum tab area, excluding the add button and any minimize/maximize/close buttons in the window _maxTabArea.Location = new Point(SystemInformation.BorderSize.Width + offset.X + screenCoordinates.X, offset.Y + screenCoordinates.Y); _maxTabArea.Width = GetMaxTabAreaWidth(tabs, offset); _maxTabArea.Height = TabHeight; // Get the width of the content area for each tab by taking the parent window's client width, subtracting the left and right border widths and the // add button area (if applicable) and then dividing by the number of tabs int tabContentWidth = Math.Min(_activeCenterImage.Width, Convert.ToInt32(Math.Floor(Convert.ToDouble(_maxTabArea.Width / tabs.Count)))); // Determine if we need to redraw the TabImage properties for each tab by seeing if the content width that we calculated above is equal to content // width we had in the previous rendering pass bool redraw = tabContentWidth != _tabContentWidth || forceRedraw; if (redraw) { _tabContentWidth = tabContentWidth; } int i = tabs.Count - 1; List <Tuple <TitleBarTab, Rectangle> > activeTabs = new List <Tuple <TitleBarTab, Rectangle> >(); // Render the background image if (_background != null) { graphicsContext.DrawImage(_background, offset.X, offset.Y, _parentWindow.Width, TabHeight); } int selectedIndex = tabs.FindIndex(t => t.Active); Image tabCenterImage = null; if (selectedIndex != -1) { TitleBarTab selectedTab = tabs[selectedIndex]; Image tabLeftImage = GetTabLeftImage(selectedTab); Image tabRightImage = GetTabRightImage(selectedTab); tabCenterImage = GetTabCenterImage(selectedTab); Rectangle tabArea = new Rectangle( SystemInformation.BorderSize.Width + offset.X + selectedIndex * (tabContentWidth + tabLeftImage.Width + tabRightImage.Width - OverlapWidth), offset.Y + (TabHeight - tabCenterImage.Height), tabContentWidth + tabLeftImage.Width + tabRightImage.Width, tabCenterImage.Height); if (IsTabRepositioning && _tabClickOffset != null) { // Make sure that the user doesn't move the tab past the beginning of the list or the outside of the window tabArea.X = cursor.X - _tabClickOffset.Value; tabArea.X = Math.Max(SystemInformation.BorderSize.Width + offset.X, tabArea.X); tabArea.X = Math.Min( SystemInformation.BorderSize.Width + (_parentWindow.WindowState == FormWindowState.Maximized ? _parentWindow.ClientRectangle.Width - (_parentWindow.ControlBox ? SystemInformation.CaptionButtonSize.Width : 0) - (_parentWindow.MinimizeBox ? SystemInformation.CaptionButtonSize.Width : 0) - (_parentWindow.MaximizeBox ? SystemInformation.CaptionButtonSize.Width : 0) : _parentWindow.ClientRectangle.Width) - tabArea.Width, tabArea.X); int dropIndex = 0; // Figure out which slot the active tab is being "dropped" over if (tabArea.X - SystemInformation.BorderSize.Width - offset.X - TabRepositionDragDistance > 0) { dropIndex = Math.Min( Convert.ToInt32( Math.Round( Convert.ToDouble(tabArea.X - SystemInformation.BorderSize.Width - offset.X - TabRepositionDragDistance) / Convert.ToDouble(tabArea.Width - OverlapWidth))), tabs.Count - 1); } // If the tab has been moved over another slot, move the tab object in the window's tab list if (dropIndex != selectedIndex) { TitleBarTab tab = tabs[selectedIndex]; _parentWindow.Tabs.SuppressEvents(); _parentWindow.Tabs.Remove(tab); _parentWindow.Tabs.Insert(dropIndex, tab); _parentWindow.Tabs.ResumeEvents(); } } activeTabs.Add(new Tuple <TitleBarTab, Rectangle>(tabs[selectedIndex], tabArea)); } // Loop through the tabs in reverse order since we need the ones farthest on the left to overlap those to their right foreach (TitleBarTab tab in ((IEnumerable <TitleBarTab>)tabs).Reverse()) { Image tabLeftImage = GetTabLeftImage(tab); tabCenterImage = GetTabCenterImage(tab); Image tabRightImage = GetTabRightImage(tab); Rectangle tabArea = new Rectangle( SystemInformation.BorderSize.Width + offset.X + (i * (tabContentWidth + tabLeftImage.Width + tabRightImage.Width - OverlapWidth)), offset.Y + (TabHeight - tabCenterImage.Height), tabContentWidth + tabLeftImage.Width + tabRightImage.Width, tabCenterImage.Height); // If we need to redraw the tab image, null out the property so that it will be recreated in the call to Render() below if (redraw) { tab.TabImage = null; } // In this first pass, we only render the inactive tabs since we need the active tabs to show up on top of everything else if (!tab.Active) { Render(graphicsContext, tab, tabArea, cursor, tabLeftImage, tabCenterImage, tabRightImage); } i--; } // In the second pass, render all of the active tabs identified in the previous pass foreach (Tuple <TitleBarTab, Rectangle> tab in activeTabs) { Image tabLeftImage = GetTabLeftImage(tab.Item1); tabCenterImage = GetTabCenterImage(tab.Item1); Image tabRightImage = GetTabRightImage(tab.Item1); Render(graphicsContext, tab.Item1, tab.Item2, cursor, tabLeftImage, tabCenterImage, tabRightImage); } _previousTabCount = tabs.Count; // Render the add tab button to the screen if (ShowAddButton && !IsTabRepositioning) { _addButtonArea = new Rectangle( (_previousTabCount * (tabContentWidth + _activeLeftSideImage.Width + _activeRightSideImage.Width - OverlapWidth)) + _activeRightSideImage.Width + AddButtonMarginLeft + offset.X, AddButtonMarginTop + offset.Y + (TabHeight - tabCenterImage.Height), _addButtonImage.Width, _addButtonImage.Height); bool cursorOverAddButton = IsOverAddButton(cursor); graphicsContext.DrawImage( cursorOverAddButton ? _addButtonHoverImage : _addButtonImage, _addButtonArea, 0, 0, cursorOverAddButton ? _addButtonHoverImage.Width : _addButtonImage.Width, cursorOverAddButton ? _addButtonHoverImage.Height : _addButtonImage.Height, GraphicsUnit.Pixel); } }
/// <summary> /// Method to create a new tab when the add button in the title bar is clicked; creates a new <see cref="ConnectionWindow"/>. /// </summary> /// <returns>Tab for a new <see cref="ConnectionWindow"/> instance.</returns> public override TitleBarTab CreateTab() { _previouslyClickedTab = new TitleBarTab(this) { Content = new ConnectionWindow() }; return _previouslyClickedTab; }
/// <summary>Tests whether the <paramref name="cursor" /> is hovering over the given <paramref name="tab" />.</summary> /// <param name="tab">Tab that we are to see if the cursor is hovering over.</param> /// <param name="cursor">Current location of the cursor.</param> /// <returns> /// True if the <paramref name="cursor" /> is within the <see cref="TitleBarTab.Area" /> of the <paramref name="tab" /> and is over a non- transparent /// pixel of <see cref="TitleBarTab.TabImage" />, false otherwise. /// </returns> protected virtual bool IsOverTab(TitleBarTab tab, Point cursor) { return(IsOverNonTransparentArea(tab.Area, tab.TabImage, cursor)); }
/// <summary> /// Gets the image to use for the right side of the <paramref name="tab"/>. /// </summary> /// <param name="tab">Tab that we are retrieving the image for.</param> /// <returns>The image for the right side of <paramref name="tab"/>.</returns> protected virtual Image GetTabRightImage(TitleBarTab tab) { return(tab.Active ? _activeRightSideImage : _inactiveRightSideImage); }
/// <summary>Tests whether the <paramref name="cursor" /> is hovering over the given <paramref name="tab" />.</summary> /// <param name="tab">Tab that we are to see if the cursor is hovering over.</param> /// <param name="cursor">Current location of the cursor.</param> /// <returns> /// True if the <paramref name="cursor" /> is within the <see cref="TitleBarTab.Area" /> of the <paramref name="tab" /> and is over a non- transparent /// pixel of <see cref="TitleBarTab.TabImage" />, false otherwise. /// </returns> protected virtual bool IsOverTab(TitleBarTab tab, Point cursor) { return IsOverNonTransparentArea(tab.Area, tab.TabImage, cursor); }
/// <summary>Checks to see if the <paramref name="cursor" /> is over the <see cref="TitleBarTab.CloseButtonArea" /> of the given <paramref name="tab" />.</summary> /// <param name="tab">The tab whose <see cref="TitleBarTab.CloseButtonArea" /> we are to check to see if it contains <paramref name="cursor" />.</param> /// <param name="cursor">Current position of the cursor.</param> /// <returns>True if the <paramref name="tab" />'s <see cref="TitleBarTab.CloseButtonArea" /> contains <paramref name="cursor" />, false otherwise.</returns> public virtual bool IsOverCloseButton(TitleBarTab tab, Point cursor) { if (!tab.ShowCloseButton || _wasTabRepositioning) { return false; } Rectangle absoluteCloseButtonArea = new Rectangle( tab.Area.X + tab.CloseButtonArea.X, tab.Area.Y + tab.CloseButtonArea.Y, tab.CloseButtonArea.Width, tab.CloseButtonArea.Height); return absoluteCloseButtonArea.Contains(cursor); }
/// <summary> /// Handler method that's called after the user finishes renaming an item in <see cref="_bookmarksListView"/>. Sets the <see cref="IConnection.Name"/> /// or <see cref="BookmarksFolder.Name"/> property and, if the user is setting the name for a newly-created bookmark, opens the option window for that /// bookmark. /// </summary> /// <param name="sender">Object from which this event originated, <see cref="_bookmarksListView"/> in this case.</param> /// <param name="e">Item being edited and its new label.</param> private void _bookmarksListView_AfterLabelEdit(object sender, LabelEditEventArgs e) { if (e.CancelEdit || String.IsNullOrEmpty(e.Label)) return; if (_listViewConnections.ContainsKey(_bookmarksListView.Items[e.Item])) { IConnection connection = _listViewConnections[_bookmarksListView.Items[e.Item]]; connection.Name = e.Label; // If this is a new connection (no Host property set) and the item label doesn't contain spaces, we default the host name for the connection to // the label name if (String.IsNullOrEmpty(connection.Host) && !e.Label.Contains(" ")) { connection.Host = e.Label; _bookmarksListView.Items[e.Item].SubItems[1].Text = e.Label; } } else _listViewFolders[_bookmarksListView.Items[e.Item]].Name = e.Label; Save(); if (_showOptionsAfterItemLabelEdit) { // Open the options window for the new bookmark if its name was set for the first time ListViewItem selectedItem = _bookmarksListView.SelectedItems[0]; TitleBarTab optionsTab = new TitleBarTab(ParentTabs) { Content = new OptionsWindow(ParentTabs) { OptionsForms = new List<Form> { ConnectionFactory.CreateOptionsForm(_listViewConnections[selectedItem]) } } }; // When the options window is closed, update the value in the host column to what was supplied by the user optionsTab.Content.FormClosed += (o, args) => selectedItem.SubItems[1].Text = _listViewConnections[selectedItem].Host; ParentTabs.Tabs.Add(optionsTab); ParentTabs.ResizeTabContents(optionsTab); ParentTabs.SelectedTab = optionsTab; _showOptionsAfterItemLabelEdit = false; } _bookmarksListView.BeginInvoke(new Action(_bookmarksListView.Sort)); }
/// <summary>Constructor; initializes the window and constructs the tab thumbnail image to use when dragging.</summary> /// <param name="tab">Tab that was torn out of its parent window.</param> /// <param name="tabRenderer">Renderer instance to use when drawing the actual tab.</param> public TornTabForm(TitleBarTab tab, BaseTabRenderer tabRenderer) { _layeredWindow = new LayeredWindow(); _initialized = false; // Set drawing styles SetStyle(ControlStyles.DoubleBuffer, true); // This should show up as a semi-transparent borderless window Opacity = 0.70; ShowInTaskbar = false; FormBorderStyle = FormBorderStyle.None; // ReSharper disable DoNotCallOverridableMethodsInConstructor BackColor = Color.Fuchsia; // ReSharper restore DoNotCallOverridableMethodsInConstructor TransparencyKey = Color.Fuchsia; AllowTransparency = true; Disposed += TornTabForm_Disposed; // Get the tab thumbnail (full size) and then draw the actual representation of the tab onto it as well Bitmap tabContents = tab.GetImage(); Bitmap contentsAndTab = new Bitmap(tabContents.Width, tabContents.Height + tabRenderer.TabHeight, tabContents.PixelFormat); Graphics tabGraphics = Graphics.FromImage(contentsAndTab); tabGraphics.DrawImage(tabContents, 0, tabRenderer.TabHeight); bool oldShowAddButton = tabRenderer.ShowAddButton; tabRenderer.ShowAddButton = false; tabRenderer.Render( new List<TitleBarTab> { tab }, tabGraphics, new Point(0, 0), new Point(0, 0), true); tabRenderer.ShowAddButton = oldShowAddButton; // Scale the thumbnail down to half size _tabThumbnail = new Bitmap(contentsAndTab.Width / 2, contentsAndTab.Height / 2, contentsAndTab.PixelFormat); Graphics thumbnailGraphics = Graphics.FromImage(_tabThumbnail); thumbnailGraphics.InterpolationMode = InterpolationMode.High; thumbnailGraphics.CompositingQuality = CompositingQuality.HighQuality; thumbnailGraphics.SmoothingMode = SmoothingMode.AntiAlias; thumbnailGraphics.DrawImage(contentsAndTab, 0, 0, _tabThumbnail.Width, _tabThumbnail.Height); Width = _tabThumbnail.Width - 1; Height = _tabThumbnail.Height - 1; _cursorOffset = new Point(tabRenderer.TabContentWidth / 4, tabRenderer.TabHeight / 4); SetWindowPosition(Cursor.Position); }