public void NewFrame(GUIContext g) { // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. if (this.MovedWindowMoveId != 0 && this.MovedWindowMoveId == g.ActiveId) { g.KeepAliveID(this.MovedWindowMoveId); Debug.Assert(this.MovedWindow != null && this.MovedWindow.RootWindow != null); Debug.Assert(this.MovedWindow.RootWindow.MoveID == this.MovedWindowMoveId); if (Mouse.Instance.LeftButtonState == KeyState.Down) { if (!this.MovedWindow.Flags.HaveFlag(WindowFlags.NoMove)) { this.MovedWindow.Position += Mouse.Instance.MouseDelta; } this.FocusWindow(this.MovedWindow); } else { g.SetActiveID(0); this.MovedWindow = null; this.MovedWindowMoveId = 0; } } else { this.MovedWindow = null; this.MovedWindowMoveId = 0; } // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow this.HoveredWindow = this.MovedWindow ?? this.FindHoveredWindow(Mouse.Instance.Position, false); if (this.HoveredWindow != null && (this.HoveredWindow.Flags.HaveFlag(WindowFlags.ChildWindow))) { this.HoveredRootWindow = this.HoveredWindow.RootWindow; } else { this.HoveredRootWindow = (this.MovedWindow != null) ? this.MovedWindow.RootWindow : this.FindHoveredWindow(Mouse.Instance.Position, true); } // Scale & Scrolling if (this.HoveredWindow != null && Mouse.Instance.MouseWheel != 0.0 && !this.HoveredWindow.Collapsed) { Window window = this.HoveredWindow; if (Keyboard.Instance.KeyDown(Key.LeftControl)) { //Scale //TODO } else { // Scroll if (!(window.Flags.HaveFlag(WindowFlags.NoScrollWithMouse))) { var newScrollY = window.Scroll.Y - Math.Sign(Mouse.Instance.MouseWheel) * 20 /*scroll step*/; float window_rounding = (float)window.WindowContainer.RuleSet.Get <double>(GUIStyleName.WindowRounding); double resize_corner_size = Math.Max(window.WindowContainer.RuleSet.FontSize * 1.35, window_rounding + 1.0 + window.WindowContainer.RuleSet.FontSize * 0.2); var contentSize = window.ContentRect.Size; var vH = window.Rect.Height - window.TitleBarHeight - window.WindowContainer.RuleSet.BorderVertical - window.WindowContainer.RuleSet.PaddingVertical; var cH = contentSize.Height; if (cH > vH) { newScrollY = MathEx.Clamp(newScrollY, 0, cH - vH); window.SetWindowScrollY(newScrollY); } } } } // Mark all windows as not visible for (int i = 0; i != this.Windows.Count; i++) { Window window = this.Windows[i]; window.WasActive = window.Active; window.Active = false; window.Accessed = false; } // No window should be open at the beginning of the frame. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. this.WindowStack.Clear(); }
public static bool Begin(string name, ref bool open, Point position, Size size, double bg_alpha = 1, WindowFlags flags = WindowFlags.VerticalScrollbar) { if (bg_alpha < 0.0f) { throw new ArgumentOutOfRangeException(nameof(bg_alpha), nameof(bg_alpha) + " cannot be negative."); } Form form = Form.current; GUIContext g = form.uiContext; WindowManager w = g.WindowManager; Debug.Assert(name != null); // Window name required Debug.Assert(g.Initialized); // Forgot to call NewFrame() Debug.Assert(g.FrameCountEnded != g.FrameCount); // Called Render() or EndFrame() and haven't called NewFrame() again yet if (flags.HaveFlag(WindowFlags.NoInputs)) { flags |= WindowFlags.NoMove | WindowFlags.NoResize; } // Find or create Window window = w.FindWindowByName(name) ?? w.CreateWindow(name, position, size, flags); // Check if this is the first call of Begin long current_frame = g.FrameCount; bool first_begin_of_the_frame = (window.LastActiveFrame != current_frame); if (first_begin_of_the_frame) { window.Flags = flags; } else { flags = window.Flags; } // Add to stack Window parent_window = w.WindowStack.Count != 0 ? w.WindowStack[w.WindowStack.Count - 1] : null; w.WindowStack.Add(window); w.CurrentWindow = window; Debug.Assert(parent_window != null || !flags.HaveFlag(WindowFlags.ChildWindow)); // Update known root window (if we are a child window, otherwise window == window->RootWindow) int root_idx; for (root_idx = w.WindowStack.Count - 1; root_idx > 0; root_idx--) { if (!(w.WindowStack[root_idx].Flags.HaveFlag(WindowFlags.ChildWindow))) { break; } } window.RootWindow = w.WindowStack[root_idx]; // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) { window.FirstUpdate(name, size, ref open, bg_alpha, flags, current_frame, parent_window); } // Inner clipping rectangle { // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior. Rect title_bar_rect = window.TitleBarRect; const float border_size = 0; var paddingHorizontal = window.WindowContainer.RuleSet.PaddingHorizontal; // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. Rect clip_rect = new Rect( new Point(Math.Floor(0.5f + title_bar_rect.Min.X + Math.Max(border_size, Math.Floor(paddingHorizontal * 0.5f))), Math.Floor(0.5f + title_bar_rect.Max.Y + border_size)), new Point(Math.Floor(0.5f + window.Position.X + window.Size.Width - Math.Max(border_size, Math.Floor(paddingHorizontal * 0.5f))), Math.Floor(0.5f + window.Position.Y + window.Size.Height - border_size))); window.ClipRect = clip_rect; //window.DrawList.AddRect(window.ClipRect.TopLeft, window.ClipRect.BottomRight, Color.Red);//test only } // Clear 'accessed' flag last thing if (first_begin_of_the_frame) { window.Accessed = false; } window.BeginCount++; // Child window can be out of sight and have "negative" clip windows. // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar). if (flags.HaveFlag(WindowFlags.ChildWindow)) { Debug.Assert(flags.HaveFlag(WindowFlags.NoTitleBar)); window.Collapsed = parent_window != null && parent_window.Collapsed; } // Return false if we don't intend to display anything to allow user to perform an early out optimization window.SkipItems = window.Collapsed || !window.Active; window.RenderTree.CurrentContainer = window.ClientAreaNode; return(!window.SkipItems); }
internal static void NewFrame() { GUIContext g = Application.ImGuiContext; WindowManager w = g.WindowManager; if (!g.Initialized) { // Initialize on first frame g.Initialized = true; } g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame; g.ConfigFlagsCurrFrame = IO.ConfigFlags; // Time g.DeltaTime = Time.deltaTime; g.Time += g.DeltaTime; g.FrameCount++; Metrics.ActiveWindows = 0; //fps var detlaTime = g.Time - g.lastFPSUpdateTime; g.lastFrameCount++; if (detlaTime > 1000) { g.fps = (int)g.lastFrameCount; g.lastFrameCount = 0; g.lastFPSUpdateTime = g.Time; } UpdateViewportsNewFrame(); #region Input //TODO move to Mouse #region mouse position if (Mouse.Instance.Position.X < 0 && Mouse.Instance.Position.Y < 0) { Mouse.Instance.Position = new Point(-9999.0f, -9999.0f); } if ((Mouse.Instance.Position.X < 0 && Mouse.Instance.Position.Y < 0) || (Mouse.Instance.LastPosition.X < 0 && Mouse.Instance.LastPosition.Y < 0)) // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta { Mouse.Instance.MouseDelta = Vector.Zero; } else { Mouse.Instance.MouseDelta = Mouse.Instance.Position - Mouse.Instance.LastPosition; } Mouse.Instance.LastPosition = Mouse.Instance.Position; #endregion #region mouse left button Mouse.Instance.LeftButtonPressed = Mouse.Instance.LeftButtonState == KeyState.Down && Mouse.Instance.LeftButtonDownDuration < 0; Mouse.Instance.LeftButtonReleased = Mouse.Instance.LeftButtonState == KeyState.Up && Mouse.Instance.LeftButtonDownDuration >= 0; Mouse.Instance.LeftButtonDownDurationPrev = Mouse.Instance.LeftButtonDownDuration; Mouse.Instance.LeftButtonDownDuration = Mouse.Instance.LeftButtonState == KeyState.Down ? (Mouse.Instance.LeftButtonDownDuration < 0 ? 0 : Mouse.Instance.LeftButtonDownDuration + g.DeltaTime) : -1; Mouse.Instance.LeftButtonDoubleClicked = false; if (Mouse.Instance.LeftButtonPressed) { if (g.Time - Mouse.Instance.LeftButtonClickedTime < Mouse.DoubleClickIntervalTimeSpan) { if ((Mouse.Instance.Position - Mouse.Instance.LeftButtonPressedPosition).LengthSquared < Mouse.DoubleClickMaxDistance * Mouse.DoubleClickMaxDistance) { Mouse.Instance.LeftButtonDoubleClicked = true; } Mouse.Instance.LeftButtonClickedTime = -99999; // so the third click isn't turned into a double-click } else { Mouse.Instance.LeftButtonClickedTime = g.Time; } Mouse.Instance.LeftButtonPressedPosition = Mouse.Instance.Position; Mouse.Instance.DragMaxDistanceSquared = 0; } else if (Mouse.Instance.LeftButtonState == KeyState.Down) { Mouse.Instance.DragMaxDistanceSquared = Math.Max(Mouse.Instance.DragMaxDistanceSquared, (Mouse.Instance.Position - Mouse.Instance.LeftButtonPressedPosition).LengthSquared); } if (Mouse.Instance.LeftButtonPressed) { ++Mouse.Instance.LeftButtonPressedTimes; } if (Mouse.Instance.LeftButtonReleased) { ++Mouse.Instance.LeftButtonReleasedTimes; } if (Mouse.Instance.LeftButtonDoubleClicked) { ++Mouse.Instance.LeftButtonDoubleClickedTimes; } #endregion #region mouse right button Mouse.Instance.RightButtonPressed = Mouse.Instance.RightButtonState == KeyState.Down && Mouse.Instance.RightButtonDownDuration < 0; Mouse.Instance.RightButtonReleased = Mouse.Instance.RightButtonState == KeyState.Up && Mouse.Instance.RightButtonDownDuration >= 0; Mouse.Instance.RightButtonDownDuration = Mouse.Instance.RightButtonState == KeyState.Down ? (Mouse.Instance.RightButtonDownDuration < 0 ? 0 : Mouse.Instance.RightButtonDownDuration + g.DeltaTime) : -1; if (Mouse.Instance.RightButtonPressed) { ++Mouse.Instance.RightButtonPressedTimes; } if (Mouse.Instance.RightButtonReleased) { ++Mouse.Instance.RightButtonReleasedTimes; } #endregion #endregion // Update HoverId data // 1. record data related to previous frame // 2. reset g.HoverId = 0; g.HoverIdAllowOverlap = false; g.HoveredIdPreviousFrame = g.HoverId; // Update ActiveId data // 1. record data related to previous frame // 2. reset if (g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) {//Clear reference to active widget if the widget isn't alive anymore g.SetActiveID(0, null); } g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = 0; g.ActiveIdPreviousFrameIsAlive = false; g.ActiveIdIsJustActivated = false; w.NewFrame(g); foreach (var form in w.Viewports) { if (!form.PlatformWindowCreated) { continue; } form.ForeBackGroundRenderOpen(); } // [DEBUG] Item picker tool - start with DebugStartItemPicker() // useful to visually select an item and break into its call-stack. UpdateDebugToolItemPicker(); // Create implicit debug window - we will only render it if the user has added something to it. GUI.Begin("Debug##Default", Application.InitialDebugWindowRect); }
internal static void Render() { GUIContext g = Application.ImGuiContext; if (MainForm.Closed) { return; } g.FrameCountRendered = g.FrameCount; Metrics.VertexNumber = 0; Metrics.IndexNumber = 0; Metrics.RenderWindows = 0; WindowManager w = g.WindowManager; //reset MeshBuffer and MeshList { foreach (var form in w.Viewports) { form.MeshBuffer.Clear(); form.MeshBuffer.Init(); form.backgroundMeshList.Clear(); form.foregroundMeshList.Clear(); } foreach (var window in w.Windows) { window.MeshList.Clear(); } } //build MeshList of all forms from their RenderTree { foreach (var form in w.Viewports) { form.RenderToBackgroundList(); } foreach (var window in w.Windows) { if (!window.Active && !Utility.HasAllFlags(window.Flags, WindowFlags.ChildWindow)) { continue; } window.RenderToMeshList(); } foreach (var form in w.Viewports) { form.RenderToForegroundList(); } } //append all MeshLists to form's MeshBuffer { foreach (var form in w.Viewports) { if (!form.PlatformWindowCreated) { continue; } if (form.Closed) { continue; } form.MeshBuffer.Append(form.backgroundMeshList); } foreach (var window in w.Windows) { if (window.Viewport == null) { continue; } if (window.Viewport.PlatformWindowCreated == false) { continue; } if (window.Viewport.Closed) { continue; } var meshBuffer = window.Viewport.MeshBuffer; meshBuffer.Append(window.MeshList); if (window.Name != Metrics.WindowName) { Metrics.VertexNumber += meshBuffer.ShapeMesh.VertexBuffer.Count + meshBuffer.ImageMesh.VertexBuffer.Count + meshBuffer.TextMesh.VertexBuffer.Count; Metrics.IndexNumber += meshBuffer.ShapeMesh.IndexBuffer.Count + meshBuffer.ImageMesh.IndexBuffer.Count + meshBuffer.TextMesh.IndexBuffer.Count; Metrics.RenderWindows++; } } foreach (var form in w.Viewports) { if (!form.PlatformWindowCreated) { continue; } if (form.Closed) { continue; } form.MeshBuffer.Append(form.foregroundMeshList); } } //render each form's MeshBuffer to back-buffer foreach (var form in w.Viewports) { if (!form.PlatformWindowCreated) { continue; } if (form.Closed) { continue; } //TODO don't render for hidden forms Application.renderer.SetRenderingWindow(form.NativeWindow); Application.renderer.Clear(form.BackgroundColor); var meshBuffer = form.MeshBuffer; Application.renderer.DrawMeshes( (int)form.ClientSize.Width, (int)form.ClientSize.Height, (meshBuffer.ShapeMesh, meshBuffer.ImageMesh, meshBuffer.TextMesh)); } //swap front and back-buffer foreach (var form in w.Viewports) { if (!form.PlatformWindowCreated) { continue; } if (form.Closed) { continue; } //TODO don't swap for hidden forms Application.renderer.SetRenderingWindow(form.NativeWindow); Application.renderer.SwapBuffers(); } //reset to render main form Application.renderer.SetRenderingWindow(MainForm.NativeWindow); }
public static void DrawTextBox(Rect rect, int id, string text, InputTextContext context, GUIState state) { GUIContext g = Form.current.uiContext; WindowManager w = g.WindowManager; Window window = w.CurrentWindow; var d = window.DrawList; var style = GUIStyle.Basic; // draw text, selection and caret var contentRect = style.GetContentRect(rect); d.PushClipRect(contentRect, true); if (g.ActiveId == id) { //Calculate positions and sizes var textContext = TextMeshUtil.GetTextContext(context.Text, rect.Size, style, GUIState.Normal); var offsetOfTextRect = contentRect.TopLeft; float pointX, pointY; float caretHeight; textContext.IndexToXY(context.CaretIndex, false, out pointX, out pointY, out caretHeight); var caretTopPoint = new Point(pointX, pointY); var caretBottomPoint = new Point(pointX, pointY + caretHeight); caretTopPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); caretBottomPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); byte caretAlpha = context.CaretAlpha; // Check if the caret is outside the rect. If so, move the text so the caret is always shown. FIXME this should be done in TextBoxBehaviour var caretX = caretTopPoint.X; if (caretX < contentRect.X || caretX > contentRect.Right) { var offsetX = -(caretX - contentRect.Width - rect.X); contentRect.Offset(offsetX, 0); caretTopPoint.Offset(offsetX, 0); caretBottomPoint.Offset(offsetX, 0); } //Draw text d.DrawText(contentRect, context.Text, style, GUIState.Normal); //Draw selection rect /* * Note: Design * * left bound right bound * ↓ ↓ * | A-----------------+ * | |CONTENT_CONTENT_C| => Line 1 => rect1 * +------B | * |ONTENT_CONTENT_CONTENT_C| => Line 2 (represents inner lines) => rect2 * | C------+ * |ONTENT_CONTENT_CO| => Line 3 => rect3 * +-----------------D * * left bound = l * right bound = r */ if (context.SelectIndex != context.CaretIndex) { float selectPointX, selectPointY; textContext.IndexToXY(context.SelectIndex, false, out selectPointX, out selectPointY, out float dummyHeight); var selectTopPoint = new Point(selectPointX, selectPointY); var selectBottomPoint = new Point(selectPointX, selectPointY + caretHeight); selectTopPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); selectBottomPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); var delta = Math.Abs(selectTopPoint.Y - caretTopPoint.Y); if (delta < caretHeight) // single line { var selectionRect = new Rect( new Point(pointX, pointY), new Point(selectPointX, selectPointY + caretHeight)); selectionRect.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); d.AddRectFilled(selectionRect.Min, selectionRect.Max, Color.Argb(100, 10, 102, 214)); } else//mutiple line { var l = contentRect.Left; var r = contentRect.Right; Point A; Point B; Point C; Point D; if (selectTopPoint.Y > caretTopPoint.Y) { A = caretTopPoint; B = caretBottomPoint; C = selectTopPoint; D = selectBottomPoint; } else { A = selectTopPoint; B = selectBottomPoint; C = caretTopPoint; D = caretBottomPoint; } // Line 1 var rect1 = new Rect(A, r - A.X, caretHeight); d.AddRectFilled(rect1, Color.Argb(100, 10, 102, 214), 12); d.AddRect(rect1.Min, rect1.Max, Color.White, 12, 15, 2); // Line 2 var rect2 = new Rect(new Point(l, B.Y), new Point(r, C.Y)); if (rect2.Height > 0.5 * caretHeight)//TODO There should more a more reasonable way to detect this: If it only has two lines, we don't draw the inner rectangle. { d.AddRectFilled(rect2, Color.Argb(100, 10, 102, 214), 12); d.AddRect(rect2.Min, rect2.Max, Color.White, 12, 15, 2); } // Line 3 var rect3 = new Rect(new Point(l, C.Y), D); d.AddRectFilled(rect3, Color.Argb(100, 10, 102, 214), 12); d.AddRect(rect3.Min, rect3.Max, Color.White, 12, 15, 2); } } //Draw caret d.PathMoveTo(caretTopPoint); d.PathLineTo(caretBottomPoint); d.PathStroke(Color.Argb(caretAlpha, 0, 0, 0), false, 2); } else { d.DrawText(contentRect, text, style, GUIState.Normal); } d.PopClipRect(); // draw the box { d.AddRect(rect.Min, rect.Max, style.GetBorderColor(state)); } }
public Window(string name, Point position, Size size, WindowFlags Flags) { Form form = Form.current; GUIContext g = form.uiContext; WindowManager w = g.WindowManager; this.ID = name.GetHashCode(); this.Name = name; this.Active = this.WasActive = false; this.Position = position; this.FullSize = size; this.Flags = Flags; this.AbsoluteVisualList = new List <Visual>(); this.RenderTree = new RenderTree(this.ID, position, size); this.RenderContext = new RenderContext(this.geometryRenderer, this.MeshList); this.IDStack.Push(this.ID); this.MoveID = this.GetID("#MOVE"); this.MeshBuffer.OwnerName = this.Name; #region Window nodes { var windowContainer = new Node(this.GetID("window"), "window"); this.WindowContainer = windowContainer; var style = windowContainer.RuleSet; style.BorderRadius = (2, 2, 2, 2); style.BorderColor = (Color.Rgb(0x707070), Color.Rgb(0x707070), Color.Rgb(0x707070), Color.Rgb(0x707070)); style.Set(StylePropertyName.BorderTopColor, Color.Blue, GUIState.Active); style.Set(StylePropertyName.BorderRightColor, Color.Blue, GUIState.Active); style.Set(StylePropertyName.BorderBottomColor, Color.Blue, GUIState.Active); style.Set(StylePropertyName.BorderLeftColor, Color.Blue, GUIState.Active); style.Set(StylePropertyName.BorderTopColor, Color.Gray, GUIState.Hover); style.Set(StylePropertyName.BorderRightColor, Color.Gray, GUIState.Hover); style.Set(StylePropertyName.BorderBottomColor, Color.Gray, GUIState.Hover); style.Set(StylePropertyName.BorderLeftColor, Color.Gray, GUIState.Hover); style.Set(StylePropertyName.BorderTop, 1.0); style.Set(StylePropertyName.BorderRight, 1.0); style.Set(StylePropertyName.BorderBottom, 1.0); style.Set(StylePropertyName.BorderLeft, 1.0); style.Set(StylePropertyName.PaddingTop, 5.0); style.Set(StylePropertyName.PaddingRight, 10.0); style.Set(StylePropertyName.PaddingBottom, 5.0); style.Set(StylePropertyName.PaddingLeft, 10.0); style.Set(StylePropertyName.WindowBorderColor, Color.Rgb(255, 0, 0), GUIState.Normal); style.Set(StylePropertyName.WindowBorderColor, Color.Rgb(0, 0, 255), GUIState.Active); style.Set(StylePropertyName.WindowShadowColor, Color.Argb(100, 227, 227, 227)); style.Set(StylePropertyName.WindowShadowWidth, 15.0); style.Set(StylePropertyName.BackgroundColor, Color.White); style.Set(StylePropertyName.ResizeGripColor, Color.Argb(0x77303030)); style.Set(StylePropertyName.ResizeGripColor, Color.Argb(0xAA303030), GUIState.Hover); style.Set(StylePropertyName.ResizeGripColor, Color.Argb(0xFF303030), GUIState.Active); style.Set(StylePropertyName.WindowRounding, 20.0); windowContainer.AttachLayoutGroup(true); windowContainer.UseBoxModel = true; var windowStyleOptions = GUILayout.Width(this.FullSize.Width).Height( this.Collapsed ? this.CollapsedHeight : this.FullSize.Height ); windowContainer.RuleSet.ApplyOptions(windowStyleOptions); this.RenderTree.Root.AppendChild(windowContainer); } //title bar if (!Flags.HaveFlag(WindowFlags.NoTitleBar)) { this.titleBar = new Node(this.GetID("titleBar"), "title bar"); titleBar.AttachLayoutGroup(false); titleBar.RuleSet.ApplyOptions(GUILayout.ExpandWidth(true).Height(this.TitleBarHeight)); titleBar.UseBoxModel = true; StyleRuleSetBuilder b = new StyleRuleSetBuilder(titleBar); b.Padding((top: 8, right: 8, bottom: 8, left: 8)) .FontColor(Color.Black) .FontSize(12) .BackgroundColor(Color.White) .AlignmentVertical(Alignment.Center) .AlignmentHorizontal(Alignment.Start); this.titleIcon = new Node(this.GetID("icon"), "icon"); titleIcon.AttachLayoutEntry(new Size(20, 20)); titleIcon.RuleSet.ApplyOptions(GUILayout.Width(20).Height(20)); titleIcon.UseBoxModel = false; this.titleText = new Node(this.GetID("title"), "title"); var contentSize = titleText.RuleSet.CalcContentBoxSize(this.Name, GUIState.Normal); titleText.AttachLayoutEntry(contentSize); titleText.RuleSet.ApplyOptions(GUILayout.Height(20)); titleText.UseBoxModel = false; var closeButton = new Node(this.GetID("close button"), "close button"); closeButton.AttachLayoutEntry(new Size(20, 20)); closeButton.RuleSet.ApplyOptions(GUILayout.Width(20).Height(20)); closeButton.UseBoxModel = false; titleBar.AppendChild(titleIcon); titleBar.AppendChild(titleText); this.WindowContainer.AppendChild(titleBar); } //client area { this.clientArea = new Node(this.GetID("client area"), "client area"); clientArea.AttachLayoutGroup(true); clientArea.RuleSet.Set(StylePropertyName.OverflowY, (int)OverflowPolicy.Scroll); clientArea.RuleSet.Set(StylePropertyName.ScrollBarWidth, CurrentOS.IsDesktopPlatform ? 10.0 : 20.0); clientArea.RuleSet.Set(StylePropertyName.ScrollBarBackgroundColor, Color.Rgb(240)); clientArea.RuleSet.Set(StylePropertyName.ScrollBarButtonColor, Color.Rgb(205), GUIState.Normal); clientArea.RuleSet.Set(StylePropertyName.ScrollBarButtonColor, Color.Rgb(166), GUIState.Hover); clientArea.RuleSet.Set(StylePropertyName.ScrollBarButtonColor, Color.Rgb(96), GUIState.Active); clientArea.RuleSet.ApplyOptions(GUILayout.ExpandWidth(true).ExpandHeight(true)); clientArea.UseBoxModel = true; clientArea.RuleSet.refNode = clientArea; #if ShowClientAreaOutline clientArea.RuleSet.OutlineWidth = 1; clientArea.RuleSet.OutlineColor = Color.DarkRed; #endif this.ClientAreaNode = clientArea; this.WindowContainer.AppendChild(clientArea); } //resize grip (lasy-initialized) if (!Flags.HaveFlag(WindowFlags.NoTitleBar)) { this.ShowWindowTitleBar(true); } this.ShowWindowClientArea(!this.Collapsed); #endregion }
public void Setup(string name, Point position, Size size, ref bool open, double bg_alpha, WindowFlags flags, long current_frame, Window parent_window) { Form form = Form.current; GUIContext g = form.uiContext; WindowManager w = g.WindowManager; this.Active = true; this.BeginCount = 0; this.ClipRect = Rect.Big; this.LastActiveFrame = current_frame; // clear draw list, setup outer clip rect this.DrawList.Clear(); this.DrawList.Init(); Rect fullScreenRect = new Rect(0, 0, form.ClientSize); if (flags.HaveFlag(WindowFlags.ChildWindow) && !flags.HaveFlag(WindowFlags.ComboBox | WindowFlags.Popup)) { this.DrawList.PushClipRect(parent_window.ClipRect, true); this.ClipRect = this.DrawList.GetCurrentClipRect(); } else { this.DrawList.PushClipRect(fullScreenRect, true); this.ClipRect = this.DrawList.GetCurrentClipRect(); } // draw outer clip rect //this.DrawList.AddRect(this.ClipRect.TopLeft, this.ClipRect.BottomRight, Color.Blue);//test only // Collapse window by double-clicking on title bar if (!(flags.HaveFlag(WindowFlags.NoTitleBar)) && !(flags.HaveFlag(WindowFlags.NoCollapse))) { if (w.HoveredWindow == this && g.IsMouseHoveringRect(this.TitleBarRect) && Mouse.Instance.LeftButtonDoubleClicked) { this.Collapsed = !this.Collapsed; w.FocusWindow(this); } } else { this.Collapsed = false; } #region size this.ApplySize(this.FullSize); this.Size = this.Collapsed ? this.TitleBarRect.Size : this.FullSize; #endregion #region position this.Position = new Point((int)this.PosFloat.X, (int)this.PosFloat.Y); if (flags.HaveFlag(WindowFlags.ChildWindow)) { this.Position = this.PosFloat = position; this.Size = this.FullSize = size; // 'size' provided by user passed via BeginChild()->Begin(). } #endregion // Draw window + handle manual resize GUIStyle style = this.Style; GUIStyle titleBarStyle = this.TitleBarStyle; Rect title_bar_rect = this.TitleBarRect; float window_rounding = (float)style.Get <double>(GUIStyleName.WindowRounding); if (this.Collapsed) { // Draw title bar only this.DrawList.AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, new Color(0.40f, 0.40f, 0.80f, 0.50f)); } else { Color resize_col = Color.Clear; double rezie_size = this.Style.Get <double>(GUIStyleName.ResizeGripSize); double resize_corner_size = Math.Max(rezie_size * 1.35, window_rounding + 1.0 + rezie_size * 0.2); if (!flags.HaveFlag(WindowFlags.AlwaysAutoResize) && !flags.HaveFlag(WindowFlags.NoResize)) { // Manual resize var br = this.Rect.BottomRight; Rect resize_rect = new Rect(br - new Vector(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br); int resize_id = this.GetID("#RESIZE"); bool hovered, held; GUIBehavior.ButtonBehavior(resize_rect, resize_id, out hovered, out held, ButtonFlags.FlattenChilds); resize_col = held ? style.Get <Color>(GUIStyleName.ResizeGripColor, GUIState.Active) : hovered ? style.Get <Color>(GUIStyleName.ResizeGripColor, GUIState.Hover) : style.Get <Color>(GUIStyleName.ResizeGripColor); if (hovered || held) { //Mouse.Instance.Cursor = Cursor.NeswResize; } if (held) { // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position var t = Mouse.Instance.Position - g.ActiveIdClickOffset - this.Position; var new_size_width = t.X + resize_rect.Width; var new_size_height = t.Y + resize_rect.Height; new_size_width = MathEx.Clamp(new_size_width, 330, fullScreenRect.Width); //min size of a window is 145x235 new_size_height = MathEx.Clamp(new_size_height, 150, fullScreenRect.Height); Size resize_size = new Size(new_size_width, new_size_height); this.ApplySize(resize_size); // adjust scroll parameters var contentSize = this.ContentRect.Size; if (contentSize != Size.Zero) { var vH = this.Rect.Height - this.TitleBarHeight - this.Style.BorderVertical - this.Style.PaddingVertical; var cH = contentSize.Height; if (cH > vH) { var oldScrollY = this.Scroll.Y; oldScrollY = MathEx.Clamp(oldScrollY, 0, cH - vH); this.Scroll.Y = oldScrollY; } } } this.Size = this.FullSize; title_bar_rect = this.TitleBarRect; } // Window background Color bg_color = style.BackgroundColor; if (bg_alpha >= 0.0f) { bg_color.A = bg_alpha; } if (bg_color.A > 0.0f) { this.DrawList.AddRectFilled(this.Position + new Vector(0, this.TitleBarHeight), this.Rect.BottomRight, bg_color, window_rounding, flags.HaveFlag(WindowFlags.NoTitleBar) ? 15 : 4 | 8); } // Title bar if (!flags.HaveFlag(WindowFlags.NoTitleBar)) { this.DrawList.AddRectFilled(title_bar_rect.TopLeft, title_bar_rect.BottomRight, w.FocusedWindow == this ? titleBarStyle.Get <Color>(GUIStyleName.BackgroundColor, GUIState.Active) : titleBarStyle.Get <Color>(GUIStyleName.BackgroundColor), window_rounding, 1 | 2); } // Render resize grip // (after the input handling so we don't have a frame of latency) if (!flags.HaveFlag(WindowFlags.NoResize)) { Point br = this.Rect.BottomRight; var borderBottom = this.Style.BorderBottom; var borderRight = this.Style.BorderRight; this.DrawList.PathLineTo(br + new Vector(-resize_corner_size, -borderBottom)); this.DrawList.PathLineTo(br + new Vector(-borderRight, -resize_corner_size)); this.DrawList.PathArcToFast( new Point(br.X - window_rounding - borderRight, br.Y - window_rounding - borderBottom), window_rounding, 0, 3); this.DrawList.PathFill(resize_col); } // Scroll bar if (flags.HaveFlag(WindowFlags.VerticalScrollbar)) { //get content size without clip var contentPosition = this.ContentRect.TopLeft; var contentSize = this.ContentRect.Size; if (contentSize != Size.Zero) { int id = this.GetID("#SCROLLY"); double scrollBarWidth = this.Style.Get <double>(GUIStyleName.ScrollBarWidth); Point scroll_TopLeft = new Point( this.Rect.Right - scrollBarWidth - this.Style.BorderRight - this.Style.PaddingRight, this.Rect.Top + this.TitleBarHeight + this.Style.BorderTop + this.Style.PaddingTop); var sH = this.Rect.Height - this.TitleBarHeight - this.Style.BorderVertical - this.Style.PaddingVertical + (flags.HaveFlag(WindowFlags.NoResize) ? 0 : -resize_corner_size); var vH = this.Rect.Height - this.TitleBarHeight - this.Style.BorderVertical - this.Style.PaddingVertical; Point scroll_BottomRight = scroll_TopLeft + new Vector(scrollBarWidth, sH); Rect bgRect = new Rect(scroll_TopLeft, scroll_BottomRight); var cH = contentSize.Height; var top = this.Scroll.Y * sH / cH; var height = sH * vH / cH; if (height < sH) { // handle mouse click/drag bool held = false; bool hovered = false; bool previously_held = (g.ActiveId == id); GUIBehavior.ButtonBehavior(bgRect, id, out hovered, out held); if (held) { top = Mouse.Instance.Position.Y - bgRect.Y - 0.5 * height; top = MathEx.Clamp(top, 0, sH - height); var targetScrollY = top * cH / sH; this.SetWindowScrollY(targetScrollY); } Point scrollButton_TopLeft = scroll_TopLeft + new Vector(0, top); Point scrllButton_BottomRight = scrollButton_TopLeft + new Vector(scrollBarWidth, height); Rect buttonRect = new Rect(scrollButton_TopLeft, scrllButton_BottomRight); //Draw vertical scroll bar and button { var bgColor = this.Style.Get <Color>(GUIStyleName.ScrollBarBackgroundColor); var buttonColor = this.Style.Get <Color>(GUIStyleName.ScrollBarButtonColor, held ? GUIState.Active : hovered ? GUIState.Hover : GUIState.Normal); this.DrawList.AddRectFilled(bgRect.TopLeft, buttonRect.TopRight, bgColor); this.DrawList.AddRectFilled(buttonRect.TopLeft, buttonRect.BottomRight, buttonColor); this.DrawList.AddRectFilled(buttonRect.BottomLeft, bgRect.BottomRight, bgColor); } } else { var bgColor = this.Style.Get <Color>(GUIStyleName.ScrollBarBackgroundColor); this.DrawList.AddRectFilled(bgRect.TopLeft, bgRect.BottomRight, bgColor); } } } this.ContentRect = Rect.Zero; } // draw title bar text if (!flags.HaveFlag(WindowFlags.NoTitleBar)) { // title text var state = w.FocusedWindow == this ? GUIState.Active : GUIState.Normal; this.DrawList.DrawBoxModel(title_bar_rect, name, titleBarStyle, state); // close button if (CloseButton(this.GetID("#CLOSE"), new Rect(title_bar_rect.TopRight + new Vector(-45, 0), title_bar_rect.BottomRight))) { open = false; } } // Borders if (flags.HaveFlag(WindowFlags.ShowBorders)) { var state = w.FocusedWindow == this ? GUIState.Active : GUIState.Normal; // window border var borderColor = this.Style.Get <Color>(GUIStyleName.WindowBorderColor, state); this.DrawList.AddRect(this.Position, this.Position + new Vector(this.Size.Width, this.Size.Height), borderColor, window_rounding); // window shadow #if false { var state = w.FocusedWindow == this ? GUIState.Active : GUIState.Normal; var shadowColor = this.Style.Get <Color>(GUIStyleName.WindowShadowColor, state); var shadowWidth = this.Style.Get <double>(GUIStyleName.WindowShadowWidth, state); var d = this.DrawList; //top-left corner d.AddRectFilledGradientTopLeftToBottomRight(this.Rect.TopLeft + new Vector(-shadowWidth, -shadowWidth), this.Rect.TopLeft, Color.Clear, shadowColor); //top d.AddRectFilledGradient(this.Rect.TopLeft + new Vector(0, -shadowWidth), this.Rect.TopRight, Color.Clear, shadowColor); d.AddRectFilledGradient(this.Rect.BottomLeft, this.Rect.BottomRight + new Vector(0, shadowWidth), shadowColor, Color.Clear); } #endif } // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() this.WindowClippedRect = this.Rect; this.WindowClippedRect.Intersect(this.ClipRect); }
internal void NewFrame() { current = this; GUIContext g = this.uiContext; WindowManager w = g.WindowManager; if (!g.Initialized) { // Initialize on first frame g.Initialized = true; } // Time g.DeltaTime = Time.deltaTime; g.Time += g.DeltaTime; g.FrameCount++; //fps var detlaTime = g.Time - g.lastFPSUpdateTime; g.lastFrameCount++; if (detlaTime > 1000) { g.fps = (int)g.lastFrameCount; g.lastFrameCount = 0; g.lastFPSUpdateTime = g.Time; } #region Input #region mouse position if (Mouse.Instance.Position.X < 0 && Mouse.Instance.Position.Y < 0) { Mouse.Instance.Position = new Point(-9999.0f, -9999.0f); } if ((Mouse.Instance.Position.X < 0 && Mouse.Instance.Position.Y < 0) || (Mouse.Instance.LastPosition.X < 0 && Mouse.Instance.LastPosition.Y < 0)) // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta { Mouse.Instance.MouseDelta = Vector.Zero; } else { Mouse.Instance.MouseDelta = Mouse.Instance.Position - Mouse.Instance.LastPosition; } Mouse.Instance.LastPosition = Mouse.Instance.Position; #endregion #region mouse left button Mouse.Instance.LeftButtonPressed = Mouse.Instance.LeftButtonState == KeyState.Down && Mouse.Instance.LeftButtonDownDuration < 0; Mouse.Instance.LeftButtonReleased = Mouse.Instance.LeftButtonState == KeyState.Up && Mouse.Instance.LeftButtonDownDuration >= 0; Mouse.Instance.LeftButtonDownDurationPrev = Mouse.Instance.LeftButtonDownDuration; Mouse.Instance.LeftButtonDownDuration = Mouse.Instance.LeftButtonState == KeyState.Down ? (Mouse.Instance.LeftButtonDownDuration < 0 ? 0 : Mouse.Instance.LeftButtonDownDuration + g.DeltaTime) : -1; Mouse.Instance.LeftButtonDoubleClicked = false; if (Mouse.Instance.LeftButtonPressed) { if (g.Time - Mouse.Instance.LeftButtonClickedTime < Mouse.DoubleClickIntervalTimeSpan) { if ((Mouse.Instance.Position - Mouse.Instance.LeftButtonPressedPos).LengthSquared < Mouse.DoubleClickMaxDistance * Mouse.DoubleClickMaxDistance) { Mouse.Instance.LeftButtonDoubleClicked = true; } Mouse.Instance.LeftButtonClickedTime = -99999; // so the third click isn't turned into a double-click } else { Mouse.Instance.LeftButtonClickedTime = g.Time; } Mouse.Instance.LeftButtonPressedPos = Mouse.Instance.Position; Mouse.Instance.DragMaxDiatanceSquared = 0; } else if (Mouse.Instance.LeftButtonState == KeyState.Down) { Mouse.Instance.DragMaxDiatanceSquared = Math.Max(Mouse.Instance.DragMaxDiatanceSquared, (Mouse.Instance.Position - Mouse.Instance.LeftButtonPressedPos).LengthSquared); } if (Mouse.Instance.LeftButtonPressed) { ++Mouse.Instance.LeftButtonPressedTimes; } if (Mouse.Instance.LeftButtonReleased) { ++Mouse.Instance.LeftButtonReleasedTimes; } if (Mouse.Instance.LeftButtonDoubleClicked) { ++Mouse.Instance.LeftButtonDoubleClickedTimes; } #endregion #region mouse right button Mouse.Instance.RightButtonPressed = Mouse.Instance.RightButtonState == KeyState.Down && Mouse.Instance.RightButtonDownDuration < 0; Mouse.Instance.RightButtonReleased = Mouse.Instance.RightButtonState == KeyState.Up && Mouse.Instance.RightButtonDownDuration >= 0; Mouse.Instance.RightButtonDownDuration = Mouse.Instance.RightButtonState == KeyState.Down ? (Mouse.Instance.RightButtonDownDuration < 0 ? 0 : Mouse.Instance.RightButtonDownDuration + g.DeltaTime) : -1; if (Mouse.Instance.RightButtonPressed) { ++Mouse.Instance.RightButtonPressedTimes; } if (Mouse.Instance.RightButtonReleased) { ++Mouse.Instance.RightButtonReleasedTimes; } #endregion #endregion // Clear reference to active widget if the widget isn't alive anymore g.HoveredIdPreviousFrame = g.HoverId; g.HoverId = 0; g.HoverIdAllowOverlap = false; if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) { g.SetActiveID(0); } g.ActiveIdPreviousFrame = g.ActiveId; g.ActiveIdIsAlive = false; g.ActiveIdIsJustActivated = false; w.NewFrame(g); // Create implicit window - we will only render it if the user has added something to it. GUI.Begin("Debug", ref this.debugWindowOpen, (0, 0), (200, 140), 0.8); }
public static bool Begin(string name, ref bool open, Point position, Size size, double bg_alpha = 1, WindowFlags flags = WindowFlags.VerticalScrollbar) { if (bg_alpha < 0.0f) { throw new ArgumentOutOfRangeException(nameof(bg_alpha), nameof(bg_alpha) + " cannot be negative."); } Form form = Form.current; GUIContext g = form.uiContext; WindowManager w = g.WindowManager; Debug.Assert(name != null); // Window name required Debug.Assert(g.Initialized); // Forgot to call NewFrame() Debug.Assert(g.FrameCountEnded != g.FrameCount); // Called Render() or EndFrame() and haven't called NewFrame() again yet if (flags.HaveFlag(WindowFlags.NoInputs)) { flags |= WindowFlags.NoMove | WindowFlags.NoResize; } // Find or create Window window = w.FindWindowByName(name) ?? w.CreateWindow(name, position, size, flags); // Check if this is the first call of Begin long current_frame = g.FrameCount; bool first_begin_of_the_frame = (window.LastActiveFrame != current_frame); if (first_begin_of_the_frame) { window.Flags = flags; } else { flags = window.Flags; } // Add to stack Window parent_window = w.WindowStack.Count != 0 ? w.WindowStack[w.WindowStack.Count - 1] : null; w.WindowStack.Add(window); w.CurrentWindow = window; Debug.Assert(parent_window != null || !flags.HaveFlag(WindowFlags.ChildWindow)); // Update known root window (if we are a child window, otherwise window == window->RootWindow) int root_idx; for (root_idx = w.WindowStack.Count - 1; root_idx > 0; root_idx--) { if (!(w.WindowStack[root_idx].Flags.HaveFlag(WindowFlags.ChildWindow))) { break; } } window.RootWindow = w.WindowStack[root_idx]; // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) { window.FirstUpdate(name, size, ref open, bg_alpha, flags, current_frame, parent_window); ImGui.Development.Metrics.ActiveWindows++; } // Clear 'accessed' flag last thing if (first_begin_of_the_frame) { window.Accessed = false; } window.BeginCount++; // Child window can be out of sight and have "negative" clip windows. // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar). if (flags.HaveFlag(WindowFlags.ChildWindow)) { Debug.Assert(flags.HaveFlag(WindowFlags.NoTitleBar)); window.Collapsed = parent_window != null && parent_window.Collapsed; window.Position = position; } // Return false if we don't intend to display anything to allow user to perform an early out optimization window.SkipItems = window.Collapsed || !window.Active; window.RenderTree.CurrentContainer = window.ClientAreaNode; return(!window.SkipItems); }
public static bool TreeNode(string label, ref bool open) { GUIContext g = GetCurrentContext(); Window window = GetCurrentWindow(); if (window.SkipItems) { return(false); } BeginVertical(label + "_Tree"); var id = window.GetID(label); // style apply var style = GUIStyle.Basic; style.PushStretchFactor(false, 1); //+1, always expand width style.PushPadding((1, 1, 1, 5)); //+4 do { // rect var lineHeight = style.GetLineHeight(); Rect rect = window.GetRect(id, new Size(0, lineHeight)); if (rect == Layout.StackLayout.DummyRect) //TODO how shold dummy rect be correctly handled in every control? { break; } // interact bool hovered, held; bool pressed = GUIBehavior.ButtonBehavior(rect, id, out hovered, out held, ButtonFlags.PressedOnClick); if (pressed) { open = !open; } // render { DrawList d = window.DrawList; var state = (hovered && held) ? GUIState.Active : hovered ? GUIState.Hover : GUIState.Normal; if (hovered || held) { style.PushBgColor(new Color(0.40f, 0.40f, 0.90f, 0.45f), GUIState.Normal); //+1 TODO It's stupid to sprcifiy style like this. There should be a better way to do this. style.PushBgColor(new Color(0.45f, 0.45f, 0.90f, 0.80f), GUIState.Hover); //+1 style.PushBgColor(new Color(0.53f, 0.53f, 0.87f, 0.80f), GUIState.Active); //+1 var color = style.Get <Color>(GUIStyleName.BackgroundColor, state); d.RenderFrame(rect.Min, rect.Max, color, false, 0); style.PopStyle(3); //-3 } d.RenderCollapseTriangle(rect.Min + new Vector(0 + style.PaddingTop, lineHeight * 0.15f), open, lineHeight, Color.White, 0.7); rect.X += rect.Height; var delta = rect.Width - rect.Height; if (delta > 0) { rect.Width = delta; } d.DrawText(rect, label, style, state); } }while(false); // style restore style.PopStyle(); //-1 style.PopStyle(4); //-4 BeginHorizontal("#Content"); Space("Space", 20); BeginVertical("#Items"); return(open); }
/// <summary> /// Create an auto-layout collapsing header. /// </summary> /// <param name="text">header text</param> /// <param name="open">opened</param> /// <returns>true when opened</returns> /// <remarks> It is horizontally stretched (factor 1).</remarks> public static bool CollapsingHeader(string text, ref bool open, float scale = 1) { GUIContext g = GetCurrentContext(); Window window = GetCurrentWindow(); if (window.SkipItems) { return(false); } var id = window.GetID(text); // style apply var style = GUIStyle.Basic; style.PushStretchFactor(false, 1); //+1, always expand width style.PushPadding(2); //4 // rect var height = style.GetLineHeight(); Rect rect = window.GetRect(id, new Size(0, height)); if (rect == Layout.StackLayout.DummyRect) //TODO how shold dummy rect be correctly handled in every control? { style.PopStyle(); //-1 style.PopStyle(4); //-4 return(false); } // interact bool hovered, held; bool pressed = GUIBehavior.ButtonBehavior(rect, id, out hovered, out held, ButtonFlags.PressedOnClick); if (pressed) { open = !open; } // render { DrawList d = window.DrawList; style.PushBgColor(new Color(0.40f, 0.40f, 0.90f, 0.45f), GUIState.Normal); //+1 TODO It's stupid to sprcifiy style like this. There should be a better way to do this. style.PushBgColor(new Color(0.45f, 0.45f, 0.90f, 0.80f), GUIState.Hover); //+1 style.PushBgColor(new Color(0.53f, 0.53f, 0.87f, 0.80f), GUIState.Active); //+1 var state = (hovered && held) ? GUIState.Active : hovered ? GUIState.Hover : GUIState.Normal; Color col = style.Get <Color>(GUIStyleName.BackgroundColor, state); d.RenderFrame(rect.Min, rect.Max, col, false, 0); style.PopStyle(3); d.RenderCollapseTriangle(rect.Min, open, rect.Height, Color.Black, scale); rect.X += rect.Height; var delta = rect.Width - rect.Height; if (delta > 0) { rect.Width = delta; } d.DrawText(rect, text, style, state); } // style restore style.PopStyle(); //-1 style.PopStyle(4); //-4 return(open); }
public bool IsHovered(Rect bb, int id, bool flattenChildren = false) { GUIContext g = this; if (g.HoverId != 0 && g.HoverId != id && !g.HoverIdAllowOverlap) { return(false); } Window window = g.WindowManager.CurrentWindow; if (g.WindowManager.HoveredWindow != window && (!flattenChildren || g.WindowManager.HoveredRootWindow != window.RootWindow)) { return(false); } if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) { return(false); } if (!IsMouseHoveringRect(bb.Min, bb.Max)) { return(false); } if (!this.WindowManager.IsWindowContentHoverable(g.WindowManager.HoveredRootWindow)) { return(false); } // [DEBUG] Item Picker tool! // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making // the cost of this tool near-zero. // TODO Consider how to get slightly better call-stack and support picking non-hovered // items. if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id) { Form.current.ForegroundDrawingContext.DrawRectangle( null, new Rendering.Pen(Color.Argb(255, 255, 255, 0), 1), bb); } if (g.DebugItemPickerBreakID == id) { //System.Diagnostics.Debugger.Break(); System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(2, true); var frames = stackTrace.GetFrames(); if (frames != null) { System.Diagnostics.StackFrame targetFrame = null; bool nextFrame = false; foreach (var frame in frames) { var methodInfo = frame.GetMethod(); if (methodInfo.IsPublic && methodInfo.DeclaringType == typeof(GUILayout)) { nextFrame = true; continue; } if (nextFrame) { targetFrame = frame; break; } } if (targetFrame != null) { string fileName = targetFrame.GetFileName(); string methodName = targetFrame.GetMethod().Name; int lineNumber = targetFrame.GetFileLineNumber(); System.Diagnostics.Debug.WriteLine($"{fileName}({lineNumber}): {methodName}"); } } } return(true); }
public void EndFrame(GUIContext g) { // Click to focus window and start moving (after we're done with all our widgets) if (g.ActiveId == 0 && g.HoverId == 0 && Mouse.Instance.LeftButtonPressed) { if (this.HoveredRootWindow != null) { this.FocusWindow(this.HoveredWindow); if (!(this.HoveredWindow.Flags.HaveFlag(WindowFlags.NoMove))) { this.MovingWindow = this.HoveredWindow; this.MovingWindowMoveId = this.HoveredRootWindow.MoveId; g.SetActiveID(this.MovingWindowMoveId, this.HoveredRootWindow); } } else if (this.FocusedWindow != null) { // Clicking on void disable focus this.FocusWindow(null); } } //TODO refactor to don't rely on flag to identify window type, subclass Window instead // Sort windows so // 1. child windows always rendered after its parent // 2. popup windows always rendered after regular window windowSwapBuffer.Clear(); childWindows.Clear(); popupWindows.Clear(); for (int i = 0; i < Windows.Count; i++) { var window = Windows[i]; if (window.Active && window.Flags.HaveFlag(WindowFlags.ChildWindow)) { childWindows.Add(window); } else if (window.Flags.HaveFlag(WindowFlags.Popup)) { popupWindows.Add(window); } else { windowSwapBuffer.Add(window); } } foreach (var childWindow in childWindows) { var parentWindowIndex = windowSwapBuffer.FindIndex(win => win == childWindow.RootWindow); if (parentWindowIndex < 0) { throw new IndexOutOfRangeException( $"Cannot find the parent window of child window<{childWindow.Name}>"); } windowSwapBuffer.Insert(parentWindowIndex + 1, childWindow); } windowSwapBuffer.AddRange(popupWindows); Debug.Assert(windowSwapBuffer.Count == Windows.Count); Windows.Clear(); Windows.AddRange(windowSwapBuffer); }
public static void DrawTextBox(Node node, int id, string text, InputTextContext context, GUIState state) { GUIContext g = Form.current.uiContext; Rect rect = node.Rect; var d = node.RenderOpen(); var style = node.RuleSet; // draw text, selection and caret var contentRect = node.ContentRect; // clip text rendering to content-box //d.PushClip(contentRect); if (g.ActiveId == id) { //Calculate positions and sizes var textContext = TextMeshUtil.GetTextContext(context.Text, rect.Size, style, GUIState.Normal); var offsetOfTextRect = contentRect.TopLeft; float pointX, pointY; float caretHeight; textContext.IndexToXY(context.CaretIndex, false, out pointX, out pointY, out caretHeight); var caretTopPoint = new Point(pointX, pointY); var caretBottomPoint = new Point(pointX, pointY + caretHeight); caretTopPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); caretBottomPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); byte caretAlpha = context.CaretAlpha; // Check if the caret is outside the rect. If so, move the text so the caret is always shown. FIXME this should be done in TextBoxBehaviour var caretX = caretTopPoint.X; if (caretX < contentRect.X || caretX > contentRect.Right) { var offsetX = -(caretX - contentRect.Width - rect.X); contentRect.Offset(offsetX, 0); caretTopPoint.Offset(offsetX, 0); caretBottomPoint.Offset(offsetX, 0); } //Draw text d.DrawText(style, context.Text, contentRect); //Draw selection rect /* * Note: Design * * left bound right bound * ↓ ↓ * | A-----------------+ * | |CONTENT_CONTENT_C| => Line 1 => rect1 * +------B | * |ONTENT_CONTENT_CONTENT_C| => Line 2 (represents inner lines) => rect2 * | C------+ * |ONTENT_CONTENT_CO| => Line 3 => rect3 * +-----------------D * * left bound = l * right bound = r */ if (context.SelectIndex != context.CaretIndex) { float selectPointX, selectPointY; textContext.IndexToXY(context.SelectIndex, false, out selectPointX, out selectPointY, out float dummyHeight); var selectTopPoint = new Point(selectPointX, selectPointY); var selectBottomPoint = new Point(selectPointX, selectPointY + caretHeight); selectTopPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); selectBottomPoint.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); var delta = Math.Abs(selectTopPoint.Y - caretTopPoint.Y); if (delta < caretHeight) // single line { var selectionRect = new Rect( new Point(pointX, pointY), new Point(selectPointX, selectPointY + caretHeight)); selectionRect.Offset(offsetOfTextRect.X, offsetOfTextRect.Y); d.DrawRectangle(new Brush(Color.Argb(100, 10, 102, 214)), null, selectionRect); } else//multiple lines { var l = contentRect.Left; var r = contentRect.Right; Point A; Point B; Point C; Point D; if (selectTopPoint.Y > caretTopPoint.Y) { A = caretTopPoint; B = caretBottomPoint; C = selectTopPoint; D = selectBottomPoint; } else { A = selectTopPoint; B = selectBottomPoint; C = caretTopPoint; D = caretBottomPoint; } // Line 1 var rect1 = new Rect(A, r - A.X, caretHeight); d.DrawRectangle(new Brush(Color.Argb(100, 10, 102, 214)), null, rect1); d.DrawRectangle(null, new Pen(Color.White, 2), rect1); // Line 2 var rect2 = new Rect(new Point(l, B.Y), new Point(r, C.Y)); if (rect2.Height > 0.5 * caretHeight)//TODO There should more a more reasonable way to detect this: If it only has two lines, we don't draw the inner rectangle. { d.DrawRectangle(new Brush(Color.Argb(100, 10, 102, 214)), null, rect2); d.DrawRectangle(null, new Pen(Color.White, 2), rect2); } // Line 3 var rect3 = new Rect(new Point(l, C.Y), D); d.DrawRectangle(new Brush(Color.Argb(100, 10, 102, 214)), null, rect3); d.DrawRectangle(null, new Pen(Color.White, 2), rect3); } } //Draw caret d.DrawLine(new Pen(Color.Argb(caretAlpha, 0, 0, 0), 2), caretTopPoint, caretBottomPoint); } else { d.DrawText(style, text, contentRect); } //d.Pop(); d.Close(); }
public Window(string name, Point position, Size size, WindowFlags Flags) { Form form = Form.current; GUIContext g = form.uiContext; this.ID = name.GetHashCode(); this.Name = name; this.IDStack.Push(this.ID); this.Flags = Flags; this.PosFloat = position; this.Position = new Point((int)PosFloat.X, (int)PosFloat.Y); this.Size = this.FullSize = size; this.DrawList = new DrawList(); this.MoveID = GetID("#MOVE"); this.Active = WasActive = false; // window styles { var style = new GUIStyle(); style.Set(GUIStyleName.BorderTop, 1.0); style.Set(GUIStyleName.BorderRight, 1.0); style.Set(GUIStyleName.BorderBottom, 1.0); style.Set(GUIStyleName.BorderLeft, 1.0); style.Set(GUIStyleName.PaddingTop, 5.0); style.Set(GUIStyleName.PaddingRight, 10.0); style.Set(GUIStyleName.PaddingBottom, 5.0); style.Set(GUIStyleName.PaddingLeft, 10.0); style.Set(GUIStyleName.WindowBorderColor, Color.Rgb(170, 170, 170), GUIState.Normal); style.Set(GUIStyleName.WindowBorderColor, Color.Rgb(24, 131, 215), GUIState.Active); style.Set(GUIStyleName.WindowShadowColor, Color.Argb(100, 227, 227, 227)); style.Set(GUIStyleName.WindowShadowWidth, 15.0); style.Set(GUIStyleName.BackgroundColor, Color.White); style.Set(GUIStyleName.ResizeGripSize, 20.0); style.Set(GUIStyleName.ResizeGripColor, Color.Argb(75, 102, 102, 102)); style.Set(GUIStyleName.ResizeGripColor, Color.Argb(150, 102, 102, 102), GUIState.Hover); style.Set(GUIStyleName.ResizeGripColor, Color.Argb(225, 102, 102, 102), GUIState.Active); style.Set(GUIStyleName.WindowRounding, 3.0); style.Set(GUIStyleName.ScrollBarWidth, CurrentOS.IsDesktopPlatform ? 10.0 : 20.0); style.Set(GUIStyleName.ScrollBarBackgroundColor, Color.Rgb(240)); style.Set(GUIStyleName.ScrollBarButtonColor, Color.Rgb(205), GUIState.Normal); style.Set(GUIStyleName.ScrollBarButtonColor, Color.Rgb(166), GUIState.Hover); style.Set(GUIStyleName.ScrollBarButtonColor, Color.Rgb(96), GUIState.Active); this.Style = style; } // window header styles { var style = new GUIStyle(); style.Set(GUIStyleName.BackgroundColor, Color.White); style.Set(GUIStyleName.BackgroundColor, Color.White, GUIState.Active); style.Set(GUIStyleName.BackgroundColor, Color.White, GUIState.Disabled); style.Set <double>(GUIStyleName.BorderTopLeftRadius, 3.0); style.Set <double>(GUIStyleName.BorderTopRightRadius, 3.0); style.Set(GUIStyleName.PaddingTop, 8.0); style.Set(GUIStyleName.PaddingRight, 8.0); style.Set(GUIStyleName.PaddingBottom, 8.0); style.Set(GUIStyleName.PaddingLeft, 8.0); style.Set(GUIStyleName.FontColor, Color.Black, GUIState.Normal); style.Set(GUIStyleName.FontColor, Color.Rgb(153, 153, 153), GUIState.Active); style.FontFamily = GUIStyle.Default.FontFamily; style.FontSize = 12.0; this.TitleBarStyle = style; } var scrollBarWidth = this.Style.Get <double>(GUIStyleName.ScrollBarWidth); var clientSize = new Size( this.Size.Width - scrollBarWidth - this.Style.PaddingHorizontal - this.Style.BorderHorizontal, this.Size.Height - this.Style.PaddingVertical - this.Style.BorderVertical - this.TitleBarHeight); this.StackLayout = new StackLayout(this.ID, clientSize); }
public Window(string name, Point position, Size size, WindowFlags Flags) { Form form = Form.current; GUIContext g = form.uiContext; WindowManager w = g.WindowManager; this.ID = name.GetHashCode(); this.Name = name; this.Active = this.WasActive = false; this.Position = position; this.FullSize = size; this.Flags = Flags; this.AbsoluteVisualList = new List <Visual>(); this.RenderTree = new RenderTree(this.ID, position, size); this.IDStack.Push(this.ID); this.MoveID = this.GetID("#MOVE"); #region Window nodes { var windowContainer = new Node(this.GetID("window"), "window"); this.WindowContainer = windowContainer; var style = windowContainer.RuleSet; style.BackgroundColor = Color.White; style.Border = (1, 1, 1, 1); style.BorderColor = (Color.Black, Color.Black, Color.Black, Color.Black); style.Set(GUIStyleName.BorderTopColor, Color.Blue, GUIState.Active); style.Set(GUIStyleName.BorderRightColor, Color.Blue, GUIState.Active); style.Set(GUIStyleName.BorderBottomColor, Color.Blue, GUIState.Active); style.Set(GUIStyleName.BorderLeftColor, Color.Blue, GUIState.Active); style.Set(GUIStyleName.BorderTopColor, Color.Gray, GUIState.Hover); style.Set(GUIStyleName.BorderRightColor, Color.Gray, GUIState.Hover); style.Set(GUIStyleName.BorderBottomColor, Color.Gray, GUIState.Hover); style.Set(GUIStyleName.BorderLeftColor, Color.Gray, GUIState.Hover); style.Set(GUIStyleName.BorderTop, 1.0); style.Set(GUIStyleName.BorderRight, 1.0); style.Set(GUIStyleName.BorderBottom, 1.0); style.Set(GUIStyleName.BorderLeft, 1.0); style.Set(GUIStyleName.PaddingTop, 5.0); style.Set(GUIStyleName.PaddingRight, 10.0); style.Set(GUIStyleName.PaddingBottom, 5.0); style.Set(GUIStyleName.PaddingLeft, 10.0); style.Set(GUIStyleName.WindowBorderColor, Color.Rgb(255, 0, 0), GUIState.Normal); style.Set(GUIStyleName.WindowBorderColor, Color.Rgb(0, 0, 255), GUIState.Active); style.Set(GUIStyleName.WindowShadowColor, Color.Argb(100, 227, 227, 227)); style.Set(GUIStyleName.WindowShadowWidth, 15.0); style.Set(GUIStyleName.BackgroundColor, Color.White); style.Set(GUIStyleName.ResizeGripColor, Color.Argb(75, 102, 102, 102)); style.Set(GUIStyleName.ResizeGripColor, Color.Argb(150, 102, 102, 102), GUIState.Hover); style.Set(GUIStyleName.ResizeGripColor, Color.Argb(225, 102, 102, 102), GUIState.Active); style.Set(GUIStyleName.WindowRounding, 20.0); style.Set(GUIStyleName.ScrollBarWidth, CurrentOS.IsDesktopPlatform ? 10.0 : 20.0); style.Set(GUIStyleName.ScrollBarBackgroundColor, Color.Rgb(240)); style.Set(GUIStyleName.ScrollBarButtonColor, Color.Rgb(205), GUIState.Normal); style.Set(GUIStyleName.ScrollBarButtonColor, Color.Rgb(166), GUIState.Hover); style.Set(GUIStyleName.ScrollBarButtonColor, Color.Rgb(96), GUIState.Active); windowContainer.AttachLayoutGroup(true); windowContainer.UseBoxModel = true; var windowStyleOptions = GUILayout.Width(this.FullSize.Width).Height( this.Collapsed ? this.CollapsedHeight : this.FullSize.Height ); windowContainer.RuleSet.ApplyOptions(windowStyleOptions); this.RenderTree.Root.AppendChild(windowContainer); } //title bar { var titleBarContainer = new Node(this.GetID("titleBar"), "title bar"); this.titleBarNode = titleBarContainer; titleBarContainer.AttachLayoutGroup(false); titleBarContainer.RuleSet.ApplyOptions(GUILayout.ExpandWidth(true).Height(this.TitleBarHeight)); titleBarContainer.UseBoxModel = true; StyleRuleSetBuilder b = new StyleRuleSetBuilder(titleBarContainer); b.Padding((top: 8, right: 8, bottom: 8, left: 8)) .FontColor(Color.Black) .FontSize(12) .BackgroundColor(Color.White) .AlignmentVertical(Alignment.Center) .AlignmentHorizontal(Alignment.Start); var icon = new Node(this.GetID("icon"), "icon"); icon.AttachLayoutEntry(new Size(20, 20)); icon.RuleSet.ApplyOptions(GUILayout.Width(20).Height(20)); icon.UseBoxModel = false; icon.Primitive = new ImagePrimitive(@"assets/images/logo.png"); var title = new Node(this.GetID("title"), "title"); var contentSize = title.RuleSet.CalcSize(this.Name, GUIState.Normal); title.AttachLayoutEntry(contentSize); title.RuleSet.ApplyOptions(GUILayout.Height(20)); title.UseBoxModel = false; title.Primitive = new TextPrimitive(this.Name); this.titleBarTitleNode = title; var closeButton = new Node(this.GetID("close button"), "close button"); closeButton.AttachLayoutEntry(new Size(20, 20)); closeButton.RuleSet.ApplyOptions(GUILayout.Width(20).Height(20)); closeButton.UseBoxModel = false; PathPrimitive path = new PathPrimitive(); path.PathRect(new Rect(0, 0, 20, 20)); closeButton.Primitive = path; titleBarContainer.AppendChild(icon); titleBarContainer.AppendChild(title); //titleBarContainer.AppendChild(closeButton); this.WindowContainer.AppendChild(titleBarContainer); } //client area { var node = new Node(this.GetID("client area"), "client area"); node.AttachLayoutGroup(true); node.RuleSet.ApplyOptions(GUILayout.ExpandWidth(true).ExpandHeight(true)); node.UseBoxModel = true; node.RuleSet.OutlineWidth = 1; node.RuleSet.OutlineColor = Color.Red; node.RuleSet.refNode = node; this.ClientAreaNode = node; this.WindowContainer.AppendChild(node); } //resize grip (lasy-initialized) this.ShowWindowTitleBar(true); this.ShowWindowClientArea(!this.Collapsed); #endregion }
/// <summary> /// Create an auto-layout vertical slider that user can drag to select a value. /// </summary> /// <param name="label">label of the slider</param> /// <param name="value">The value the slider shows.</param> /// <param name="minValue">The value at the top end of the slider.</param> /// <param name="maxValue">The value at the bottom end of the slider.</param> /// <returns>The value set by the user.</returns> /// <remarks>minValue <= value <= maxValue</remarks> public static double VSlider(string label, double value, double minValue, double maxValue) { GUIContext g = GetCurrentContext(); Window window = GetCurrentWindow(); if (window.SkipItems) { return(value); } var id = window.GetID(label); // style apply var style = GUIStyle.Basic; // rect Size size = style.CalcSize(label, GUIState.Normal); style.PushStretchFactor(true, 1);//+1 { var minSilderHeight = 200; size.Width = 20; size.Height += minSilderHeight; } var rect = window.GetRect(id); // interact var spacing = GUISkin.Instance.InternalStyle.Get <double>(GUIStyleName._ControlLabelSpacing); var labelHeight = GUISkin.Instance.InternalStyle.Get <double>(GUIStyleName._LabelHeight); var sliderHeight = rect.Height - spacing - labelHeight; if (sliderHeight <= 0) { sliderHeight = 1; } var sliderRect = new Rect(rect.X, rect.Y, rect.Width, sliderHeight); bool hovered, held; value = GUIBehavior.SliderBehavior(sliderRect, id, false, value, minValue, maxValue, out hovered, out held); // render var state = GUI.Normal; if (hovered) { state = GUI.Hover; } if (g.ActiveId == id && Mouse.Instance.LeftButtonState == KeyState.Down) { state = GUI.Active; } GUIAppearance.DrawVSlider(rect, label, value, minValue, maxValue, state, sliderRect, labelHeight); // style restore style.PopStyle();//-1 return(value); }
public static string TextBoxBehavior(int id, Rect rect, string text, out bool hovered, out bool active, out InputTextContext context, InputTextFlags flags = 0, Func <char, bool> checker = null) { GUIContext g = Form.current.uiContext; active = false; hovered = g.IsHovered(rect, id); if (hovered) { g.SetHoverID(id); if (Mouse.Instance.LeftButtonPressed) { g.SetActiveID(id); } } else { if (Mouse.Instance.LeftButtonPressed) { if (g.ActiveId == id) { g.SetActiveID(0); } } } if (g.ActiveId == id && g.InputTextState.inputTextContext.Id != id) //editing text box changed to TextBox<id> { g.InputTextState.stateMachine.CurrentState = "Active"; //reset state g.InputTextState.inputTextContext = new InputTextContext() //reset input text context data { Id = id, CaretIndex = 0, SelectIndex = 0, Selecting = false, CaretPosition = Point.Zero, Flags = flags, Checker = checker, Rect = rect, Text = text }; } context = null; if (g.ActiveId == id) { active = true; var stateMachine = g.InputTextState.stateMachine; context = g.InputTextState.inputTextContext; context.Rect = rect; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. g.ActiveIdAllowOverlap = !Mouse.Instance.LeftButtonPressed; #if INSPECT_STATE var A = stateMachine.CurrentState; #endif if (Mouse.Instance.LeftButtonPressed) { stateMachine.MoveNext(TextBoxCommand.MoveCaret, context); } if (hovered && Mouse.Instance.LeftButtonState == KeyState.Down && stateMachine.CurrentState != TextBoxState.ActiveSelecting) { stateMachine.MoveNext(TextBoxCommand.MoveCaret, context); stateMachine.MoveNext(TextBoxCommand.BeginSelect, context); } if (hovered && Mouse.Instance.LeftButtonState == KeyState.Up) { stateMachine.MoveNext(TextBoxCommand.EndSelect, context); } if (stateMachine.CurrentState == TextBoxState.Active) { stateMachine.MoveNext(TextBoxCommand.DoEdit, context); } if (stateMachine.CurrentState == TextBoxState.ActiveSelecting) { stateMachine.MoveNext(TextBoxCommand.DoSelect, context); } stateMachine.MoveNext(TextBoxCommand.MoveCaretKeyboard, context); #if INSPECT_STATE var B = stateMachine.CurrentState; Debug.WriteLineIf(A != B, string.Format("TextBox<{0}> {1}=>{2} CaretIndex: {3}, SelectIndex: {4}", id, A, B, context.CaretIndex, context.SelectIndex)); #endif return(context.Text); } return(text); }
internal static bool DoPolygonButton(Rect rect, IReadOnlyList <Point> points, Rect textRect, string text) { Form form = Form.current; GUIContext g = form.uiContext; Window window = g.WindowManager.CurrentWindow; DrawList d = window.DrawList; int id = window.GetID(text); var mousePos = Mouse.Instance.Position; var clicked = false; var hovered = MathEx.IsPointInPolygon(mousePos, points, new Vector(rect.X, rect.Y)); textRect.Offset(rect.X, rect.Y); //control logic g.KeepAliveID(id); if (hovered) { g.SetHoverID(id); if (Mouse.Instance.LeftButtonPressed)//start track { g.SetActiveID(id); } if (Mouse.Instance.LeftButtonReleased)//end track { clicked = true; g.SetActiveID(GUIContext.None); } } // ui representation var state = GUI.Normal; if (hovered) { state = GUI.Hover; if (g.ActiveId == id && Mouse.Instance.LeftButtonState == KeyState.Down) { state = GUI.Active; } } // ui painting { var style = GUISkin.Instance[GUIControlName.PolygonButton]; d.PathClear(); foreach (var point in points) { d.PathMoveTo(point + new Vector(rect.X, rect.Y)); } d.PathFill(style.FillColor); d.PathClear(); foreach (var point in points) { d.PathMoveTo(point + new Vector(rect.X, rect.Y)); } d.PathStroke(style.LineColor, true, 2); d.DrawBoxModel(textRect, text, style, state); } return(clicked); }
public static bool ButtonBehavior(Rect bb, int id, out bool outHovered, out bool outHeld, ButtonFlags flags = 0) { GUIContext g = Form.current.uiContext; WindowManager w = g.WindowManager; Window window = w.CurrentWindow; if (flags.HaveFlag(ButtonFlags.Disabled)) { outHovered = false; outHeld = false; if (g.ActiveId == id) { g.SetActiveID(0); } return(false); } if (!flags.HaveFlag(ButtonFlags.PressedOnClickRelease | ButtonFlags.PressedOnClick | ButtonFlags.PressedOnRelease | ButtonFlags.PressedOnDoubleClick)) { flags |= ButtonFlags.PressedOnClickRelease; } bool pressed = false; bool hovered = g.IsHovered(bb, id, flags.HaveFlag(ButtonFlags.FlattenChilds)); if (hovered) { g.SetHoverID(id); if (!flags.HaveFlag(ButtonFlags.NoKeyModifiers) || (!Keyboard.Instance.KeyPressed(Key.LeftControl) && !Keyboard.Instance.KeyPressed(Key.LeftShift) && !Keyboard.Instance.KeyPressed(Key.LeftAlt))) { // | CLICKING | HOLDING with ButtonFlags.Repeat // PressedOnClickRelease | <on release>* | <on repeat> <on repeat> .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds // PressedOnClick | <on click> | <on click> <on repeat> <on repeat> .. // PressedOnRelease | <on release> | <on repeat> <on repeat> .. (NOT on release) // PressedOnDoubleClick | <on dclick> | <on dclick> <on repeat> <on repeat> .. if (flags.HaveFlag(ButtonFlags.PressedOnClickRelease) && Mouse.Instance.LeftButtonPressed) { g.SetActiveID(id, window); // Hold on ID w.FocusWindow(window); g.ActiveIdClickOffset = Mouse.Instance.Position - bb.Min; } if (((flags.HaveFlag(ButtonFlags.PressedOnClick) && Mouse.Instance.LeftButtonPressed) || (flags.HaveFlag(ButtonFlags.PressedOnDoubleClick) && Mouse.Instance.LeftButtonDoubleClicked))) { pressed = true; g.SetActiveID(0); w.FocusWindow(window); } if (flags.HaveFlag(ButtonFlags.PressedOnRelease) && Mouse.Instance.LeftButtonReleased) { if (!(flags.HaveFlag(ButtonFlags.Repeat) && Mouse.Instance.LeftButtonDownDurationPrev >= Keyboard.KeyRepeatDelay)) // Repeat mode trumps <on release> { pressed = true; } g.SetActiveID(0); } // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. if (flags.HaveFlag(ButtonFlags.Repeat) && g.ActiveId == id && Mouse.Instance.LeftButtonDownDuration > 0.0f && g.IsMouseLeftButtonClicked(true)) { pressed = true; } } } bool held = false; if (g.ActiveId == id) { if (Mouse.Instance.LeftButtonState == KeyState.Down) { held = true; } else { if (hovered && flags.HaveFlag(ButtonFlags.PressedOnClickRelease)) { if (!(flags.HaveFlag(ButtonFlags.Repeat) && Mouse.Instance.LeftButtonDownDurationPrev >= Keyboard.KeyRepeatDelay)) // Repeat mode trumps <on release> { pressed = true; } } g.SetActiveID(0); } } // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. if (hovered && flags.HaveFlag(ButtonFlags.AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) { hovered = pressed = held = false; } outHovered = hovered; outHeld = held; return(pressed); }
public static bool Begin(string name, ref bool open, Point position, Size size, double bg_alpha = 1, WindowFlags flags = WindowFlags.VerticalScrollbar) { Form form = Form.current; GUIContext g = form.uiContext; WindowManager w = g.WindowManager; Debug.Assert(name != null); // Window name required Debug.Assert(g.Initialized); // Forgot to call NewFrame() Debug.Assert(g.FrameCountEnded != g.FrameCount); // Called Render() or EndFrame() and haven't called NewFrame() again yet if (flags.HaveFlag(WindowFlags.NoInputs)) { flags |= WindowFlags.NoMove | WindowFlags.NoResize; } // Find or create Window window = w.FindWindowByName(name) ?? w.CreateWindow(name, position, size, flags); // Check if this is the first call of Begin long current_frame = g.FrameCount; bool first_begin_of_the_frame = (window.LastActiveFrame != current_frame); if (first_begin_of_the_frame) { window.Flags = flags; } else { flags = window.Flags; } // Add to stack Window parent_window = w.WindowStack.Count != 0 ? w.WindowStack[w.WindowStack.Count - 1] : null; w.WindowStack.Add(window); w.CurrentWindow = window; Debug.Assert(parent_window != null || !flags.HaveFlag(WindowFlags.ChildWindow)); // Update known root window (if we are a child window, otherwise window == window->RootWindow) int root_idx; for (root_idx = w.WindowStack.Count - 1; root_idx > 0; root_idx--) { if (!(w.WindowStack[root_idx].Flags.HaveFlag(WindowFlags.ChildWindow))) { break; } } window.RootWindow = w.WindowStack[root_idx]; // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) { window.Active = true; window.BeginCount = 0; window.ClipRect = Rect.Big; window.LastActiveFrame = current_frame; // clear draw list, setup outer clip rect window.DrawList.Clear(); window.DrawList.Init(); Rect fullScreenRect = new Rect(0, 0, form.ClientSize); if (flags.HaveFlag(WindowFlags.ChildWindow) && !flags.HaveFlag(WindowFlags.ComboBox | WindowFlags.Popup)) { window.DrawList.PushClipRect(parent_window.ClipRect, true); window.ClipRect = window.DrawList.GetCurrentClipRect(); } else { window.DrawList.PushClipRect(fullScreenRect, true); window.ClipRect = window.DrawList.GetCurrentClipRect(); } // draw outer clip rect //window.DrawList.AddRect(window.ClipRect.TopLeft, window.ClipRect.BottomRight, Color.Blue);//test only // Collapse window by double-clicking on title bar if (!(flags.HaveFlag(WindowFlags.NoTitleBar)) && !(flags.HaveFlag(WindowFlags.NoCollapse))) { if (w.HoveredWindow == window && g.IsMouseHoveringRect(window.TitleBarRect) && Mouse.Instance.LeftButtonDoubleClicked) { window.Collapsed = !window.Collapsed; w.FocusWindow(window); } } else { window.Collapsed = false; } #region size window.ApplySize(window.FullSize); window.Size = window.Collapsed ? window.TitleBarRect.Size : window.FullSize; #endregion #region position window.Position = new Point((int)window.PosFloat.X, (int)window.PosFloat.Y); if (flags.HaveFlag(WindowFlags.ChildWindow)) { window.Position = window.PosFloat = position; window.Size = window.FullSize = size; // 'size' provided by user passed via BeginChild()->Begin(). } #endregion // Draw window + handle manual resize GUIStyle style = window.Style; GUIStyle titleBarStyle = window.TitleBarStyle; Rect title_bar_rect = window.TitleBarRect; float window_rounding = (float)style.Get <double>(GUIStyleName.WindowRounding); if (window.Collapsed) { // Draw title bar only window.DrawList.AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, new Color(0.40f, 0.40f, 0.80f, 0.50f)); } else { Color resize_col = Color.Clear; double rezie_size = window.Style.Get <double>(GUIStyleName.ResizeGripSize); double resize_corner_size = Math.Max(rezie_size * 1.35, window_rounding + 1.0 + rezie_size * 0.2); if (!flags.HaveFlag(WindowFlags.AlwaysAutoResize) && !flags.HaveFlag(WindowFlags.NoResize)) { // Manual resize var br = window.Rect.BottomRight; Rect resize_rect = new Rect(br - new Vector(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br); int resize_id = window.GetID("#RESIZE"); bool hovered, held; GUIBehavior.ButtonBehavior(resize_rect, resize_id, out hovered, out held, ButtonFlags.FlattenChilds); resize_col = held ? style.Get <Color>(GUIStyleName.ResizeGripColor, GUIState.Active) : hovered?style.Get <Color>(GUIStyleName.ResizeGripColor, GUIState.Hover) : style.Get <Color>(GUIStyleName.ResizeGripColor); if (hovered || held) { //Mouse.Instance.Cursor = Cursor.NeswResize; } if (held) { // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position var t = Mouse.Instance.Position - g.ActiveIdClickOffset - window.Position; var new_size_width = t.X + resize_rect.Width; var new_size_height = t.Y + resize_rect.Height; new_size_width = MathEx.Clamp(new_size_width, 330, fullScreenRect.Width);//min size of a window is 145x235 new_size_height = MathEx.Clamp(new_size_height, 150, fullScreenRect.Height); Size resize_size = new Size(new_size_width, new_size_height); window.ApplySize(resize_size); // adjust scroll parameters var contentSize = window.ContentRect.Size; if (contentSize != Size.Zero) { var vH = window.Rect.Height - window.TitleBarHeight - window.Style.BorderVertical - window.Style.PaddingVertical; var cH = contentSize.Height; if (cH > vH) { var oldScrollY = window.Scroll.Y; oldScrollY = MathEx.Clamp(oldScrollY, 0, cH - vH); window.Scroll.Y = oldScrollY; } } } window.Size = window.FullSize; title_bar_rect = window.TitleBarRect; } // Window background Color bg_color = style.BackgroundColor; if (bg_alpha >= 0.0f) { bg_color.A = bg_alpha; } if (bg_color.A > 0.0f) { window.DrawList.AddRectFilled(window.Position + new Vector(0, window.TitleBarHeight), window.Rect.BottomRight, bg_color, window_rounding, flags.HaveFlag(WindowFlags.NoTitleBar) ? 15 : 4 | 8); } // Title bar if (!flags.HaveFlag(WindowFlags.NoTitleBar)) { window.DrawList.AddRectFilled(title_bar_rect.TopLeft, title_bar_rect.BottomRight, w.FocusedWindow == window ? titleBarStyle.Get <Color>(GUIStyleName.BackgroundColor, GUIState.Active) : titleBarStyle.Get <Color>(GUIStyleName.BackgroundColor), window_rounding, 1 | 2); } // Render resize grip // (after the input handling so we don't have a frame of latency) if (!flags.HaveFlag(WindowFlags.NoResize)) { Point br = window.Rect.BottomRight; var borderBottom = window.Style.BorderBottom; var borderRight = window.Style.BorderRight; window.DrawList.PathLineTo(br + new Vector(-resize_corner_size, -borderBottom)); window.DrawList.PathLineTo(br + new Vector(-borderRight, -resize_corner_size)); window.DrawList.PathArcToFast(new Point(br.X - window_rounding - borderRight, br.Y - window_rounding - borderBottom), window_rounding, 0, 3); window.DrawList.PathFill(resize_col); } // Scroll bar if (flags.HaveFlag(WindowFlags.VerticalScrollbar)) { //get content size without clip var contentPosition = window.ContentRect.TopLeft; var contentSize = window.ContentRect.Size; if (contentSize != Size.Zero) { int id = window.GetID("#SCROLLY"); double scrollBarWidth = window.Style.Get <double>(GUIStyleName.ScrollBarWidth); Point scroll_TopLeft = new Point( window.Rect.Right - scrollBarWidth - window.Style.BorderRight - window.Style.PaddingRight, window.Rect.Top + window.TitleBarHeight + window.Style.BorderTop + window.Style.PaddingTop); var sH = window.Rect.Height - window.TitleBarHeight - window.Style.BorderVertical - window.Style.PaddingVertical - resize_corner_size; var vH = window.Rect.Height - window.TitleBarHeight - window.Style.BorderVertical - window.Style.PaddingVertical; Point scroll_BottomRight = scroll_TopLeft + new Vector(scrollBarWidth, sH); Rect bgRect = new Rect(scroll_TopLeft, scroll_BottomRight); var cH = contentSize.Height; var top = window.Scroll.Y * sH / cH; var height = sH * vH / cH; if (height < sH) { // handle mouse click/drag bool held = false; bool hovered = false; bool previously_held = (g.ActiveId == id); GUIBehavior.ButtonBehavior(bgRect, id, out hovered, out held); if (held) { top = Mouse.Instance.Position.Y - bgRect.Y - 0.5 * height; top = MathEx.Clamp(top, 0, sH - height); var targetScrollY = top * cH / sH; window.SetWindowScrollY(targetScrollY); } Point scrollButton_TopLeft = scroll_TopLeft + new Vector(0, top); Point scrllButton_BottomRight = scrollButton_TopLeft + new Vector(scrollBarWidth, height); Rect buttonRect = new Rect(scrollButton_TopLeft, scrllButton_BottomRight); //Draw vertical scroll bar and button { var bgColor = window.Style.Get <Color>(GUIStyleName.ScrollBarBackgroundColor); var buttonColor = window.Style.Get <Color>(GUIStyleName.ScrollBarButtonColor, held ? GUIState.Active : hovered ? GUIState.Hover : GUIState.Normal); window.DrawList.AddRectFilled(bgRect.TopLeft, buttonRect.TopRight, bgColor); window.DrawList.AddRectFilled(buttonRect.TopLeft, buttonRect.BottomRight, buttonColor); window.DrawList.AddRectFilled(buttonRect.BottomLeft, bgRect.BottomRight, bgColor); } } else { var bgColor = window.Style.Get <Color>(GUIStyleName.ScrollBarBackgroundColor); window.DrawList.AddRectFilled(bgRect.TopLeft, bgRect.BottomRight, bgColor); } } } window.ContentRect = Rect.Zero; } // draw title bar text if (!flags.HaveFlag(WindowFlags.NoTitleBar)) { var state = w.FocusedWindow == window ? GUIState.Active : GUIState.Normal; window.DrawList.DrawBoxModel(title_bar_rect, name, titleBarStyle, state); } // Borders if (flags.HaveFlag(WindowFlags.ShowBorders)) { var state = w.FocusedWindow == window ? GUIState.Active : GUIState.Normal; // window border var borderColor = window.Style.Get <Color>(GUIStyleName.WindowBorderColor, state); window.DrawList.AddRect(window.Position, window.Position + new Vector(window.Size.Width, window.Size.Height), borderColor, window_rounding); // window shadow #if false { var state = w.FocusedWindow == window ? GUIState.Active : GUIState.Normal; var shadowColor = window.Style.Get <Color>(GUIStyleName.WindowShadowColor, state); var shadowWidth = window.Style.Get <double>(GUIStyleName.WindowShadowWidth, state); var d = window.DrawList; //top-left corner d.AddRectFilledGradientTopLeftToBottomRight(window.Rect.TopLeft + new Vector(-shadowWidth, -shadowWidth), window.Rect.TopLeft, Color.Clear, shadowColor); //top d.AddRectFilledGradient(window.Rect.TopLeft + new Vector(0, -shadowWidth), window.Rect.TopRight, Color.Clear, shadowColor); d.AddRectFilledGradient(window.Rect.BottomLeft, window.Rect.BottomRight + new Vector(0, shadowWidth), shadowColor, Color.Clear); } #endif } // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() window.WindowClippedRect = window.Rect; window.WindowClippedRect.Intersect(window.ClipRect); } // Inner clipping rectangle { // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior. Rect title_bar_rect = window.TitleBarRect; const float border_size = 0; var paddingHorizontal = window.Style.PaddingHorizontal; // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. Rect clip_rect = new Rect( new Point(Math.Floor(0.5f + title_bar_rect.Min.X + Math.Max(border_size, Math.Floor(paddingHorizontal * 0.5f))), Math.Floor(0.5f + title_bar_rect.Max.Y + border_size)), new Point(Math.Floor(0.5f + window.Position.X + window.Size.Width - Math.Max(border_size, Math.Floor(paddingHorizontal * 0.5f))), Math.Floor(0.5f + window.Position.Y + window.Size.Height - border_size))); window.DrawList.PushClipRect(clip_rect, true); window.ClipRect = clip_rect; //window.DrawList.AddRect(window.ClipRect.TopLeft, window.ClipRect.BottomRight, Color.Red);//test only } // Clear 'accessed' flag last thing if (first_begin_of_the_frame) { window.Accessed = false; } window.BeginCount++; // Child window can be out of sight and have "negative" clip windows. // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar). if (flags.HaveFlag(WindowFlags.ChildWindow)) { Debug.Assert(flags.HaveFlag(WindowFlags.NoTitleBar)); window.Collapsed = parent_window != null && parent_window.Collapsed; } window.StackLayout.Begin(); // Return false if we don't intend to display anything to allow user to perform an early out optimization window.SkipItems = window.Collapsed || !window.Active; return(!window.SkipItems); }