/// <summary> /// Function called during idle time. /// </summary> /// <returns><b>true</b> to continue execution, <b>false</b> to stop.</returns> private bool Idle() { _swap.RenderTargetView.Clear(GorgonColor.White); var windowSize = new DX.Size2F(ClientSize.Width, ClientSize.Height); var imageSize = new DX.Size2F(_texture.Width, _texture.Height); // Calculate the scale between the images. var scale = new DX.Size2F(windowSize.Width / imageSize.Width, windowSize.Height / imageSize.Height); // Only scale on a single axis if we don't have a 1:1 aspect ratio. if (scale.Height > scale.Width) { scale.Height = scale.Width; } else { scale.Width = scale.Height; } // Scale the image. var size = new DX.Size2((int)(scale.Width * imageSize.Width), (int)(scale.Height * imageSize.Height)); // Find the position. var bounds = new DX.Rectangle((int)((windowSize.Width / 2) - (size.Width / 2)), (int)((windowSize.Height / 2) - (size.Height / 2)), size.Width, size.Height); _graphics.DrawTexture(_texture, bounds); GorgonExample.BlitLogo(_graphics); _swap.Present(1); return(true); }
protected override SizeF CalculateInnerDesiredSize(SizeF totalSize) { // Return the biggest available child extents SizeF childSize; SizeF maxChildSize = new SizeF(float.NaN, float.NaN); foreach (FrameworkElement child in GetVisibleChildren()) { childSize = new SizeF(totalSize.Width, totalSize.Height); child.Measure(ref childSize); if (float.IsNaN(maxChildSize.Width) || childSize.Width > maxChildSize.Width) { maxChildSize.Width = childSize.Width; } if (float.IsNaN(maxChildSize.Height) || childSize.Height > maxChildSize.Height) { maxChildSize.Height = childSize.Height; } } if (_ellipsisControl == null) { _ellipsisControl = CreateEllipsisControl(); } childSize = new SizeF(totalSize.Width, totalSize.Height); _ellipsisControl.Measure(ref childSize); if (float.IsNaN(maxChildSize.Width) || childSize.Width > maxChildSize.Width) { maxChildSize.Width = childSize.Width; } if (float.IsNaN(maxChildSize.Height) || childSize.Height > maxChildSize.Height) { maxChildSize.Height = childSize.Height; } return(maxChildSize); }
/// <summary> /// Handles the AfterSwapChainResized event of the Screen control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="AfterSwapChainResizedEventArgs"/> instance containing the event data.</param> private void Screen_AfterSwapChainResized(object sender, AfterSwapChainResizedEventArgs e) { _halfSize = new DX.Size2F(e.Size.Width / 2.0f, e.Size.Height / 2.0f); // Update the image. DrawAPrettyPicture(); }
private void InitializeBackBuffer(D2D.DeviceContext deviceContext, SharpDX.Size2F size) { this.backBitmap?.Dispose(); Size2 pixelSize = Helpers.GetPixelSize(size, this.Factory.DesktopDpi); var p = new D2D.BitmapProperties1( new D2D.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Premultiplied), this.Factory.DesktopDpi.Width, this.Factory.DesktopDpi.Height, D2D.BitmapOptions.Target); var desc = new D3D11.Texture2DDescription() { ArraySize = 1, BindFlags = D3D11.BindFlags.RenderTarget | D3D11.BindFlags.ShaderResource, CpuAccessFlags = D3D11.CpuAccessFlags.None, Format = DXGI.Format.B8G8R8A8_UNorm, MipLevels = 1, OptionFlags = D3D11.ResourceOptionFlags.Shared, Usage = D3D11.ResourceUsage.Default, SampleDescription = new DXGI.SampleDescription(1, 0), Width = pixelSize.Width, Height = pixelSize.Height, }; using (var buffer = new D3D11.Texture2D(this.Device, desc)) using (var surface = buffer.QueryInterface <DXGI.Surface>()) { this.backBitmap = new D2D.Bitmap1(this.DeviceContext, surface, p); } this.DeviceContext.Target = this.backBitmap; }
protected void PrepareVisual() { FrameworkElement visual = Visual; if (_preparedVisual != null && _preparedVisual != visual) { _preparedVisual.SetElementState(ElementState.Available); _preparedVisual.Deallocate(); _preparedVisual = null; } if (_screen == null) { return; } if (visual == null) { return; } if (AutoLayoutContent) { // We must bypass normal layout or the visual will be layed out to screen/skin size visual.SetScreen(_screen); if (visual.ElementState == ElementState.Available) { visual.SetElementState(ElementState.Running); } // Here is _screen != null, which means we are allocated visual.Allocate(); SizeF size = _vertsBounds.Size; visual.Measure(ref size); visual.Arrange(new RectangleF(0, 0, _vertsBounds.Size.Width, _vertsBounds.Size.Height)); } _preparedVisual = visual; }
/// <summary>Function to draw the texture.</summary> /// <param name="renderer">The renderer used to draw the texture.</param> /// <param name="image">The image being rendered.</param> /// <param name="batchState">The currently active batch render state.</param> protected override void OnDrawTexture(Gorgon2D renderer, IImageContent image, Gorgon2DBatchState batchState) { // We can use this for 3D textures because the texture is in slot 1, and slot 0, where the 2D texture is usually located is vacant and not used by the pixel shader. renderer.DrawFilledRectangle(TextureBounds, new GorgonColor(GorgonColor.White, Alpha), null, new DX.RectangleF(0, 0, 1, 1), 0, textureSampler: GorgonSamplerState.PointFiltering); renderer.End(); // Draw a frame around the volume rendering area. DX.RectangleF volRegion = _volRenderer.VolumeRegion; renderer.Begin(); DX.Size2F textArea = renderer.DefaultFont.MeasureLine(Resources.GORIMG_TEXT_3DVIEW, false); renderer.DrawFilledRectangle(volRegion, new GorgonColor(GorgonColor.Black, 0.5f)); renderer.DrawFilledRectangle(new DX.RectangleF(volRegion.Left - 1, volRegion.Bottom, volRegion.Width + 2, textArea.Height + 6), GorgonColor.White); renderer.DrawRectangle(new DX.RectangleF(volRegion.X - 1, volRegion.Y - 1, volRegion.Width + 2, volRegion.Height + 2), GorgonColor.White); renderer.DrawString("3D View", new DX.Vector2((volRegion.Right + volRegion.Left) / 2.0f - (textArea.Width / 2.0f), volRegion.Bottom + 3), color: GorgonColor.Black); renderer.End(); _volRenderer.Render(); return; }
/// <summary> /// Function to perform the button layout. /// </summary> private static void LayoutGUI() { float maxWidth = 0; var position = new DX.Vector2(0, 70); for (int i = 0; i < _buttons.Length; ++i) { if (_buttons[i] != null) { _buttons[i].Click -= Button_Click; } _buttons[i] = new Button(_compositor.Passes[i]); DX.Size2F size = _renderer.DefaultFont.MeasureText(_buttons[i].Text, false); maxWidth = maxWidth.Max(size.Width); _buttons[i].Bounds = new DX.RectangleF(0, position.Y, 0, size.Height); position = new DX.Vector2(0, position.Y + size.Height + 2); } for (int i = 0; i < _buttons.Length; ++i) { Button button = _buttons[i]; button.Bounds = new DX.RectangleF(button.Bounds.Left, button.Bounds.Top, maxWidth, button.Bounds.Height); button.Click += Button_Click; } }
protected void CycleTextures(Texture nextTexture, RectangleF textureClip, RightAngledRotation nextRotation) { TryDispose(ref _lastTexture); // Current -> Last _lastTexture = _currentTexture; _lastRawSourceSize = _currentClippedSize; _lastTextureClip = _currentTextureClip; _lastImageContext = _imageContext; // Next -> Current SizeF textureSize; _currentTexture = CreateTextureCopy(nextTexture, out textureSize); _currentTextureSize = textureSize; UpdateTextureClip(textureClip); _imageContext = new ImageContext { FrameSize = _frameSize, ShaderEffect = Effect, Rotation = nextRotation, HorizontalTextureAlignment = HorizontalTextureAlignment, VerticalTextureAlignment = VerticalTextureAlignment }; StartTransition(); FireChanged(); }
protected void LayoutLine(IList <FrameworkElement> children, PointF pos, LineMeasurement line) { float offset = 0; for (int i = line.StartIndex; i <= line.EndIndex; i++) { FrameworkElement layoutChild = children[i]; SizeF desiredChildSize = layoutChild.DesiredSize; SizeF size; PointF location; if (Orientation == Orientation.Horizontal) { size = new SizeF(desiredChildSize.Width, line.TotalExtendsInNonOrientationDirection); location = new PointF(pos.X + offset, pos.Y); ArrangeChildVertical(layoutChild, layoutChild.VerticalAlignment, ref location, ref size); offset += desiredChildSize.Width; } else { size = new SizeF(line.TotalExtendsInNonOrientationDirection, desiredChildSize.Height); location = new PointF(pos.X, pos.Y + offset); ArrangeChildHorizontal(layoutChild, layoutChild.HorizontalAlignment, ref location, ref size); offset += desiredChildSize.Height; } layoutChild.Arrange(SharpDXExtensions.CreateRectangleF(location, size)); } }
protected SizeF CalculateDesiredSize(SizeF totalSize, bool measureChildren, out float desiredColumnWidth, out float desiredRowHeight) { desiredColumnWidth = _actualColumns == 0 ? float.NaN : (int)totalSize.Width / _actualColumns; // Can be float.NaN desiredRowHeight = _actualRows == 0 ? float.NaN : (int)totalSize.Height / _actualRows; // Can be float.NaN SizeF childSize; foreach (FrameworkElement child in GetVisibleChildren()) { if (measureChildren) { childSize = new SizeF(totalSize.Width / _actualColumns, totalSize.Height / _actualRows); child.Measure(ref childSize); } else { childSize = child.DesiredSize; } if (float.IsNaN(desiredColumnWidth) || childSize.Width > desiredColumnWidth) { desiredColumnWidth = childSize.Width; } if (float.IsNaN(desiredRowHeight) || childSize.Height > desiredRowHeight) { desiredRowHeight = childSize.Height; } } return(new SizeF(desiredColumnWidth * _actualColumns, desiredRowHeight * _actualRows)); }
protected override SizeF CalculateInnerDesiredSize(SizeF totalSize) { base.CalculateInnerDesiredSize(totalSize); // Needs to be called in each sub class of Control, see comment in Control.CalculateInnerDesiredSize() AllocFont(); // Measure the text float totalWidth = totalSize.Width; // Attention: totalWidth is cleaned up by SkinContext.Zoom if (!double.IsNaN(Width)) { totalWidth = (float)Width; } SizeF size = new SizeF(); string[] lines = _asset.GetLines(totalWidth, Wrap); size.Width = 0; foreach (string line in lines) { size.Width = Math.Max(size.Width, _asset.TextWidth(line)); } size.Height = _asset.TextHeight(Math.Max(lines.Length, 1)); // Add one pixel to compensate rounding errors. Stops the label scrolling even though there is enough space. size.Width += 1; size.Height += 1; return(size); }
public void PerformLayout(RenderContext localRenderContext) { if (!_performLayout) { return; } _performLayout = false; // Setup background brush if (Background != null) { SizeF actualSize = new SizeF((float)ActualWidth, (float)ActualHeight); RectangleF rect = new RectangleF(ActualPosition.X /*- 0.5f*/, ActualPosition.Y /*- 0.5f*/, actualSize.Width /*+ 0.5f*/, actualSize.Height /*+ 0.5f*/); PositionColoredTextured[] verts = new PositionColoredTextured[6]; verts[0].Position = new Vector3(rect.Left, rect.Top, 1.0f); verts[1].Position = new Vector3(rect.Left, rect.Bottom, 1.0f); verts[2].Position = new Vector3(rect.Right, rect.Bottom, 1.0f); verts[3].Position = new Vector3(rect.Left, rect.Top, 1.0f); verts[4].Position = new Vector3(rect.Right, rect.Top, 1.0f); verts[5].Position = new Vector3(rect.Right, rect.Bottom, 1.0f); Background.SetupBrush(this, ref verts, localRenderContext.ZOrder, true); PrimitiveBuffer.SetPrimitiveBuffer(ref _backgroundContext, ref verts, PrimitiveType.TriangleList); } else { PrimitiveBuffer.DisposePrimitiveBuffer(ref _backgroundContext); } }
public void Prepare() { lock (_syncObj) { _root.Allocate(); _root.InvalidateLayout(true, true); // Prepare run. In the prepare run, the screen uses some shortcuts to set values. ScreenState = State.Preparing; SizeF skinSize = new SizeF(SkinWidth, SkinHeight); _root.UpdateLayoutRoot(skinSize); // Switch to "Running" state which builds the final screen structure ScreenState = State.Running; int maxNumUpdate = 10; // This violates against MP2 multi threading guidelines, but is required to execute this synchronously i.e. to restore state information // directly when the screen is created. TriggerScreenPreparingEvent_Sync(); while ((_root.IsMeasureInvalid || _root.IsArrangeInvalid) && maxNumUpdate-- > 0) { SetValues(); // It can be necessary to call UpdateLayoutRoot multiple times because UI elements sometimes initialize template controls/styles etc. // in the first Measure() call, which then need to invalidate the element tree again. That can happen multiple times. _root.UpdateLayoutRoot(skinSize); } HandleScheduledFocus(); } }
protected override SizeF CalculateInnerDesiredSize(SizeF totalSize) { float totalDesiredHeight = 0; float totalDesiredWidth = 0; SizeF childSize; if (Orientation == Orientation.Vertical) { foreach (FrameworkElement child in GetVisibleChildren()) { childSize = new SizeF(totalSize.Width, float.NaN); child.Measure(ref childSize); totalDesiredHeight += childSize.Height; if (childSize.Width > totalDesiredWidth) { totalDesiredWidth = childSize.Width; } } } else { foreach (FrameworkElement child in GetVisibleChildren()) { childSize = new SizeF(float.NaN, totalSize.Height); child.Measure(ref childSize); totalDesiredWidth += childSize.Width; if (childSize.Height > totalDesiredHeight) { totalDesiredHeight = childSize.Height; } } } return(new SizeF(totalDesiredWidth, totalDesiredHeight)); }
/// <summary> /// Function to convert a size value from texel coordinates to pixel space. /// </summary> /// <param name="texelSize">The texel size to convert.</param> /// <returns>A size value containing the texel space coordinates.</returns> public DX.Size2 ToPixel(DX.Size2F texelSize) { float width = Texture.Width; float height = Texture.Height; return(new DX.Size2((int)(texelSize.Width * width), (int)(texelSize.Height * height))); }
/// <summary> /// A helper method that adjusts a given frame size so that an image fitted within it not be scaled by /// the transformation used to scale the skin to the graphics device (window). /// </summary> /// <param name="frameSize">The skin relative size.</param> /// <returns>The passed size pre-scaled to compensate for any Skin-to-GraphicsDevice transformations.</returns> public static SizeF AdjustForSkinAR(SizeF frameSize) { // Adjust target size to match final Skin scaling frameSize.Width *= GraphicsDevice.Width / (float)SkinContext.SkinResources.SkinWidth; frameSize.Height *= GraphicsDevice.Height / (float)SkinContext.SkinResources.SkinHeight; return(frameSize); }
/// <summary> /// Starts a rendering operation where two images are mixed together using a transition effect. /// </summary> /// <param name="renderContext">The current rendering context.</param> /// <param name="mixValue">A value between 0.0 and 1.0 that governs how much each image contributes to the final rendering.</param> /// <param name="startContext">The <see cref="ImageContext"/> data for the starting position of the transition /// (this context is the end point).</param> /// <param name="targetEndImageSize">The size, the final end image should take within the frame. This size is given in the same /// orientation as the <paramref name="endTexture"/>, i.e. it is not rotated.</param> /// <param name="endTexture">A texture object containing the end image.</param> /// <param name="endTextureClip">The section of the end texture that should be rendered. Values are between 0 and 1.</param> /// <param name="borderColor">The color to use outside the image's boundaries.</param> /// <param name="startFrameData">Additional data to be used by the starting image shaders.</param> /// <param name="endFrameData">Additional data to be used by the ending image shaders.</param> /// <returns><c>true</c> if the rendering operation was started.</returns> public bool StartRenderTransition(RenderContext renderContext, float mixValue, ImageContext startContext, SizeF targetEndImageSize, Texture endTexture, RectangleF endTextureClip, Color borderColor, Vector4 startFrameData, Vector4 endFrameData) { RefreshParameters(targetEndImageSize, endTexture, endTextureClip); if (_effectTransition == null) { _effectTransition = ContentManager.Instance.GetEffect(GetTransitionEffectName()); } if (_lastTexture == null || _effectTransition == null) { return(false); } // Apply effect parameters _effectTransition.Parameters[PARAM_OPACITY] = (float)renderContext.Opacity; _effectTransition.Parameters[PARAM_RELATIVE_TRANSFORM] = _inverseRelativeTransformCache; _effectTransition.Parameters[PARAM_BRUSH_TRANSFORM] = _imageTransform; _effectTransition.Parameters[PARAM_FRAME_DATA] = endFrameData; _effectTransition.Parameters[PARAM_MIX_AB] = mixValue; startContext.ApplyTransitionParametersAsStartingSource(_effectTransition, startFrameData); // Disable antialiasing for image rendering. GraphicsDevice.Device.SetRenderState(RenderState.MultisampleAntialias, false); // Set border colour for area outside of texture boundaries GraphicsDevice.Device.SetSamplerState(0, SamplerState.BorderColor, borderColor.ToBgra()); GraphicsDevice.Device.SetSamplerState(1, SamplerState.BorderColor, borderColor.ToBgra()); // Render _effectTransition.StartRender(_lastTexture, renderContext.Transform); return(true); }
/// <summary> /// Function to update the camera data on the GPU. /// </summary> /// <param name="camera">The camera to update.</param> public void UpdateCamera(IGorgon2DCamera camera) { if (camera.AllowUpdateOnResize) { var viewportSize = new DX.Size2F(Graphics.Viewports[0].Width, Graphics.Viewports[0].Height); if (!camera.ViewDimensions.Equals(viewportSize)) { camera.ViewDimensions = viewportSize; } } bool camChanged = (_current != camera) || (camera.NeedsUpdate); if (!camChanged) { return; } camera.GetViewMatrix(out DX.Matrix view); camera.GetProjectionMatrix(out DX.Matrix projection); // Build the view/projection matrix. DX.Matrix.Multiply(ref view, ref projection, out _viewProjectionMatrix); CameraBuffer.Buffer.SetData(ref _viewProjectionMatrix); _current = camera; }
protected static RectangleF CalculateZoomRect(SizeF outerSize, SizeF innerSize, int zoomType) { float left; float top; switch (zoomType) { case 0: // centered, centered left = (outerSize.Width - innerSize.Width) / 2f; top = (outerSize.Height - innerSize.Height) / 2f; break; case 2: // Width centered, top unchanged left = (outerSize.Width - innerSize.Width) / 2f; top = 0; break; case 8: // Heigth centered, left unchanged left = 0; top = (outerSize.Height - innerSize.Height) / 2f; break; case 6: // Width centered, bottom unchanged left = (outerSize.Width - innerSize.Width) / 2f; top = outerSize.Height - innerSize.Height; break; case 4: // Height centered, right unchanged left = outerSize.Width - innerSize.Width; top = (outerSize.Height - innerSize.Height) / 2f; break; case 1: // Top left unchanged left = 0; top = 0; break; case 3: // Top right unchanged left = outerSize.Width - innerSize.Width; top = 0; break; case 7: // Bottom left unchanged left = 0; top = outerSize.Height - innerSize.Height; break; case 5: // Bottom right unchanged left = outerSize.Width - innerSize.Width; top = outerSize.Height - innerSize.Height; break; default: top = 0; left = 0; break; } return(new RectangleF(left / outerSize.Width, top / outerSize.Height, innerSize.Width / outerSize.Width, innerSize.Height / outerSize.Height)); }
/// <summary> /// Initializes a new instance of the <see cref="TextBuffer"/> class. /// </summary> /// <param name="fontName">The name of the font to use.</param> /// <param name="size">The font size.</param> public TextBuffer(string fontName, float size) { SetFont(fontName, size); _kerning = true; _lastTimeUsed = DateTime.MinValue; _lastTextSize = new SizeF(); ResetScrollPosition(); }
protected static float NormalizeOutputSizeToImageSize(SizeF imageSize, SizeF outputSize, Stretch stretch) { // Calculate zoom for best fit (largest image length (X/Y) fits completely in output area) float zoomFactorX = imageSize.Width / outputSize.Width; float zoomFactorY = imageSize.Height / outputSize.Height; return(zoomFactorX > zoomFactorY == (stretch == Stretch.Uniform) ? zoomFactorX : zoomFactorY); }
protected override GraphicsPath CreateBorderRectPath(RectangleF innerBorderRect) { SizeF headerLabelSize = _headerLabel.DesiredSize; return(GraphicsPathHelper.CreateRoundedRectWithTitleRegionPath(innerBorderRect, (float)CornerRadius, (float)CornerRadius, true, HEADER_INSET_LINE, headerLabelSize.Width + HEADER_INSET_SPACE * 2)); }
/// <summary> /// Raises the <see cref="E:System.Windows.Forms.Form.Load"></see> event. /// </summary> /// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param> protected override void OnLoad(EventArgs e) { base.OnLoad(e); GorgonExample.ShowStatistics = false; Cursor.Current = Cursors.WaitCursor; try { Show(); Application.DoEvents(); // Initialize Gorgon // Set it up so that we won't be rendering in the background, but allow the screensaver to activate. IReadOnlyList <IGorgonVideoAdapterInfo> adapters = GorgonGraphics.EnumerateAdapters(log: GorgonApplication.Log); if (adapters.Count == 0) { throw new GorgonException(GorgonResult.CannotCreate, "No suitable video adapter found in the system.\nGorgon requires a minimum of a Direct3D 11.4 capable video device."); } _graphics = new GorgonGraphics(adapters[0]); // Set the video mode. ClientSize = new Size(640, 400); _screen = new GorgonSwapChain(_graphics, this, new GorgonSwapChainInfo("FunWithShapes SwapChain") { Width = ClientSize.Width, Height = ClientSize.Height, Format = BufferFormat.R8G8B8A8_UNorm }); _screen.AfterSwapChainResized += Screen_AfterSwapChainResized; _graphics.SetRenderTarget(_screen.RenderTargetView); _halfSize = new DX.Size2F(_screen.Width / 2.0f, _screen.Height / 2.0f); // Create our 2D renderer so we can draw stuff. _renderer = new Gorgon2D(_graphics); LabelPleaseWait.Visible = false; GorgonExample.LoadResources(_graphics); // Draw the image. DrawAPrettyPicture(); } catch (Exception ex) { GorgonExample.HandleException(ex); GorgonApplication.Quit(); } finally { Cursor.Current = Cursors.Default; } }
protected override void MeasureBorder(SizeF totalSize) { const float realHeaderInset = HEADER_INSET_LINE + HEADER_INSET_SPACE; float borderInsetX = GetBorderCornerInsetX(); SizeF headerSize = new SizeF(totalSize.Width - (borderInsetX + realHeaderInset) * 2, totalSize.Height); _headerLabel.Measure(ref headerSize); base.MeasureBorder(totalSize); }
protected override void RefreshParameters(SizeF targetImageSize, Texture texture, RectangleF textureClip) { // If necessary update our image transformation to best fit the frame if (_refresh || texture != _lastTexture || Math.Abs(targetImageSize.Width - _lastImageSize.Width) > FLOAT_EQUALITY_LIMIT || Math.Abs(targetImageSize.Height - _lastImageSize.Height) > FLOAT_EQUALITY_LIMIT || textureClip != _lastTextureClip) { _lastTexture = texture; _lastTextureClip = textureClip; // Convert image dimensions to texture space // We're doing the relative transform first in the shader, that's why we just have to rotate the frame size Vector4 textureRect = new Vector4(0.0f, 0.0f, (targetImageSize.Width + 1.0f) / _rotatedFrameSize.Width, (targetImageSize.Height + 1.0f) / _rotatedFrameSize.Height); //Set alignment of texture within target textureRect.X = GetHorizontalAlignment(textureRect.Z); textureRect.Y = GetVerticalAlignment(textureRect.W); //Brownard 30/01/16: Do we need to take into account in the calculations below that we now allow aligning of textures? // Compensate for texture surface borders textureRect.Z /= textureClip.Width; textureRect.W /= textureClip.Height; // Determine correct 2D transform for mapping the texture to the correct place float repeatx = 1.0f / textureRect.Z; float repeaty = 1.0f / textureRect.W; if (repeatx < 0.001f || repeaty < 0.001f) { _effect = null; _refresh = true; return; } _inverseRelativeTransformCache = TranslateRotation(_rotation); _inverseRelativeTransformCache.Invert(); _imageTransform = new Vector4(textureRect.X * repeatx - textureClip.X, textureRect.Y * repeaty - textureClip.Y, repeatx, repeaty); // Build our effects _effect = ContentManager.Instance.GetEffect(GetEffectName()); // The transition effect will be allocated when required _effectTransition = null; // Trigger refresh event so that the calling class can update any custom parameters if (OnRefresh != null) { OnRefresh(); } _lastImageSize = targetImageSize; _refresh = false; } }
protected virtual void RefreshParameters(SizeF targetImageSize, Texture texture, RectangleF textureClip) { if (_refresh || _lastTexture != texture) { // Build our effects _lastTexture = texture; _effect = ContentManager.Instance.GetEffect(GetEffectName()); _refresh = false; } }
protected static RectangleF CalculatePanRect(SizeF outerSize, SizeF innerSize, float panX, float panY) { float panFactorX = (panX + 1) * 0.5f; float panFactorY = (panY + 1) * 0.5f; float left = (outerSize.Width - innerSize.Width) * panFactorX; float top = (outerSize.Height - innerSize.Height) * panFactorY; return(new RectangleF(left / outerSize.Width, top / outerSize.Height, innerSize.Width / outerSize.Width, innerSize.Height / outerSize.Height)); }
/// <summary> /// Function to measure the specified text using this font. /// </summary> /// <param name="text">The text to measure.</param> /// <param name="useOutline"><b>true</b> to include the outline in the measurement, <b>false</b> to exclude.</param> /// <param name="tabSpaceCount">[Optional] The number of spaces represented by a tab control character.</param> /// <param name="lineSpacing">[Optional] The factor used to determine the amount of space between each line.</param> /// <param name="wordWrapWidth">[Optional] The maximum width to return if word wrapping is required.</param> /// <returns>A vector containing the width and height of the text when rendered using this font.</returns> /// <remarks> /// <para> /// This will measure the specified <paramref name="text"/> and return the size, in pixels, of the region containing the text. /// </para> /// <para> /// If the <paramref name="wordWrapWidth"/> is specified and greater than zero, then word wrapping is assumed to be on and the text will be handled using word wrapping. /// </para> /// <para> /// If the <paramref name="useOutline"/> parameter is <b>true</b>, then the outline size is taken into account when measuring, otherwise only the standard glyph size is taken into account. If the font /// <see cref="HasOutline"/> property is <b>false</b>, then this parameter is ignored. /// </para> /// <para> /// The <paramref name="lineSpacing"/> parameter adjusts the amount of space between each line by multiplying it with the <see cref="FontHeight"/> value (and the <see cref="IGorgonFontInfo.OutlineSize"/> * 2 /// if <paramref name="useOutline"/> is <b>true</b> and <see cref="HasOutline"/> is <b>true</b>). For example, to achieve a double spacing effect, change this value to 2.0f. /// </para> /// <para> /// If measuring a single line of text with no breaks (i.e. newline or carriage return), and no word wrapping, then call the <see cref="MeasureLine"/> method instead for better performance. /// </para> /// </remarks> /// <seealso cref="MeasureLine"/> public DX.Size2F MeasureText(string text, bool useOutline, int tabSpaceCount = 4, float lineSpacing = 1.0f, float?wordWrapWidth = null) { if (string.IsNullOrEmpty(text)) { return(DX.Size2F.Zero); } string formattedText = FormatStringForRendering(text, tabSpaceCount); DX.Size2F result = DX.Size2F.Zero; if (wordWrapWidth != null) { formattedText = WordWrap(text, wordWrapWidth.Value); } string[] lines = formattedText.GetLines(); float fontHeight = FontHeight.FastFloor(); if (lines.Length == 0) { return(result); } if (lineSpacing.EqualsEpsilon(1.0f)) { result.Height = lines.Length * fontHeight; } else { // For a modified line spacing, we have to adjust for the last line not being affected by the line spacing. result.Height = ((lines.Length - 1) * (((fontHeight) * lineSpacing))) + (fontHeight); } if ((HasOutline) && (useOutline)) { result.Height += Info.OutlineSize * 0.5f; } // Get width. // ReSharper disable once ForCanBeConvertedToForeach for (int i = 0; i < lines.Length; ++i) { float lineWidth = GetLineWidth(lines[i], useOutline && HasOutline); if ((HasOutline) && (useOutline)) { lineWidth += Info.OutlineSize; } result.Width = result.Width.Max(lineWidth); } return(result); }
public override void Render(RenderContext renderContext, Stretch stretchMode, StretchDirection stretchDirection) { Allocate(); Texture currentTexture = CurrentTexture; SizeF currentRawSourceSize = CurrentRawSourceSize; RectangleF currentTextureClip = CurrentTextureClip; Vector4 frameData = new Vector4(currentRawSourceSize.Width, currentRawSourceSize.Height, (float)EffectTimer, 0); if (_transitionActive) { double elapsed = (SkinContext.FrameRenderingStartTime - _transitionStart).TotalSeconds / Math.Max(TransitionDuration, 0.01); if (elapsed > 1.0) { _transitionActive = false; } else { Texture lastTexture = LastTexture; SizeF lastRawSourceSize = LastRawSourceSize; RectangleF lastTextureClip = LastTextureClip; Vector4 lastFrameData = new Vector4(lastRawSourceSize.Width, lastRawSourceSize.Height, (float)EffectTimer, 0); Texture start = lastTexture ?? NullTexture.Texture; Texture end = currentTexture ?? NullTexture.Texture; if (start != end) { SizeF startSize = StretchSource(_lastImageContext.RotatedFrameSize, lastRawSourceSize, stretchMode, stretchDirection); SizeF endSize = StretchSource(_imageContext.RotatedFrameSize, currentRawSourceSize, stretchMode, stretchDirection); // Render transition from last texture to current texture _lastImageContext.Update(startSize, start, lastTextureClip); if (_imageContext.StartRenderTransition(renderContext, (float)elapsed, _lastImageContext, endSize, end, currentTextureClip, BorderColor, lastFrameData, frameData)) { _primitiveBuffer.Render(0); _imageContext.EndRenderTransition(); } } return; } } if (IsAllocated) { SizeF sourceSize = StretchSource(_imageContext.RotatedFrameSize, currentRawSourceSize, stretchMode, stretchDirection); if (_imageContext.StartRender(renderContext, sourceSize, currentTexture, currentTextureClip, BorderColor, frameData)) { _primitiveBuffer.Render(0); _imageContext.EndRender(); } } }
// Be careful to always call this method from subclasses. This is necessary to satisfy the Measure/Arrange contract for our // TemplateControl (if only ArrangeOverride() is called but not CalculateInnerDesiredSize(), the TemplateControl could be // arranged without having been measured - which is illegal). protected override SizeF CalculateInnerDesiredSize(SizeF totalSize) { FrameworkElement templateControl = _initializedTemplateControl; if (templateControl == null) { return(new SizeF()); } templateControl.Measure(ref totalSize); return(totalSize); }
protected override bool BeginRenderEffectOverride(Texture texture, RenderContext renderContext) { if (_refresh) { _effectContext = new EffectContext(); _refresh = false; } RectangleF rect = renderContext.OccupiedTransformedBounds; SizeF frameSize = new SizeF(rect.Width, rect.Height); _effectContext.ExtraParameters = GetShaderParameters(); _effectContext.ShaderEffect = _shaderEffectName; Vector4 lastFrameData = new Vector4(rect.Width, rect.Height, 0.0f, 0.0f); _effectContext.StartRender(renderContext, frameSize, texture, CROP_FULLSIZE, Color.Transparent, lastFrameData); return true; }
protected override bool BeginRenderEffectOverride(Texture texture, RenderContext renderContext) { if (_refresh) { _imageContext = new ImageContext(); _refresh = false; } RectangleF rect = renderContext.OccupiedTransformedBounds; SizeF frameSize = new SizeF(rect.Width, rect.Height); _imageContext.FrameSize = frameSize; _imageContext.ExtraParameters = GetShaderParameters(); _imageContext.ShaderEffect = SkinResources.EFFECTS_SUB_DIRECTORY + '\\' + _partialShaderEffect; Vector4 lastFrameData = new Vector4(rect.Width, rect.Height, 0.0f, 0.0f); _imageContext.StartRender(renderContext, frameSize, texture, CROP_FULLSIZE, Color.Transparent, lastFrameData); return true; }
/// <summary> /// Removes the given <paramref name="margin"/> from the specified <param name="size"/> parameter. /// </summary> /// <remarks> /// <see cref="float.NaN"/> values will be preserved, i.e. if a <paramref name="size"/> coordinate /// is <see cref="float.NaN"/>, it won't be changed. /// </remarks> /// <param name="size">Size parameter where the margin will be removed.</param> /// <param name="margin">Margin to be removed.</param> public static void RemoveMargin(ref SizeF size, Thickness margin) { if (!float.IsNaN(size.Width)) size.Width -= margin.Left + margin.Right; if (!float.IsNaN(size.Height)) size.Height -= margin.Top + margin.Bottom; }
public static bool SameSize(SizeF size1, SizeF size2) { return SameValue(size1.Width, size2.Width) && SameValue(size1.Height, size2.Height); }
/// <summary> /// This is a helper provided to assist derived Sources when scaling their content to /// the owner size. /// </summary> /// <param name="target">The total available space.</param> /// <param name="source">The unscaled source size.</param> /// <param name="stretchMode">The <see cref="Stretch"/> mode that determines which stretching technique to use.</param> /// <param name="direction">The <see cref="StretchDirection"/> that determines when to perform scaling.</param> /// <returns>The scaled source size, which may be larger than the <paramref name="target"/> size.</returns> public SizeF StretchSource(SizeF target, SizeF source, Stretch stretchMode, StretchDirection direction) { if (direction == StretchDirection.DownOnly && source.Width <= target.Width && source.Height <= target.Height) return source; if (direction == StretchDirection.UpOnly && source.Width >= target.Width && source.Height >= target.Height) return source; switch (stretchMode) { case Stretch.None: // Original size break; case Stretch.Fill: // Stretch to fit source = target; break; case Stretch.Uniform: // Keep aspect ratio and show borders { float ratio = System.Math.Min(target.Width / source.Width, target.Height / source.Height); source.Width *= ratio; source.Height *= ratio; } break; case Stretch.UniformToFill: // Keep aspect ratio, zoom in to avoid borders { float ratio = System.Math.Max(target.Width / source.Width, target.Height / source.Height); source.Width *= ratio; source.Height *= ratio; } break; } return source; }
/// <summary> /// Arranges the child vertical in a given area. If the area is bigger than the child's desired /// size, the child will be arranged according to the given <paramref name="alignment"/>. /// </summary> /// <param name="child">The child to arrange. The child will not be changed by this method.</param> /// <param name="alignment">Alignment in vertical direction.</param> /// <param name="location">Input: The starting position of the available area. Output: The position /// the child should be located.</param> /// <param name="childSize">Input: The available area for the <paramref name="child"/>. Output: /// The area the child should take.</param> public void ArrangeChildVertical(FrameworkElement child, VerticalAlignmentEnum alignment, ref PointF location, ref SizeF childSize) { // See comment in ArrangeChild SizeF desiredSize = child.DesiredSize; if (!double.IsNaN(desiredSize.Height) && desiredSize.Height <= childSize.Height) { // Height takes precedence over Stretch - Use Center as fallback if (alignment == VerticalAlignmentEnum.Center || (alignment == VerticalAlignmentEnum.Stretch && !double.IsNaN(child.Height))) { location.Y += (childSize.Height - desiredSize.Height) / 2; childSize.Height = desiredSize.Height; } else if (alignment == VerticalAlignmentEnum.Bottom) { location.Y += childSize.Height - desiredSize.Height; childSize.Height = desiredSize.Height; } else if (alignment == VerticalAlignmentEnum.Top) { // Leave location unchanged childSize.Height = desiredSize.Height; } //else if (child.VerticalAlignment == VerticalAlignmentEnum.Stretch) // - Use all the space, nothing to do here } }
public static bool SameSize(SizeF? size1, SizeF size2) { return size1.HasValue && SameSize(size1.Value, size2); }
public override void Setup(RectangleF ownerRect, float zOrder, bool skinNeutralAR) { PositionColoredTextured[] verts = new PositionColoredTextured[4]; // Upper left verts[0].X = ownerRect.Left; verts[0].Y = ownerRect.Top; verts[0].Color = 0; verts[0].Tu1 = 0.0f; verts[0].Tv1 = 0.0f; verts[0].Z = zOrder; // Bottom left verts[1].X = ownerRect.Left; verts[1].Y = ownerRect.Bottom; verts[1].Color = 0; verts[1].Tu1 = 0.0f; verts[1].Tv1 = 1.0f; verts[1].Z = zOrder; // Bottom right verts[2].X = ownerRect.Right; verts[2].Y = ownerRect.Bottom; verts[2].Color = 0; verts[2].Tu1 = 1.0f; verts[2].Tv1 = 1.0f; verts[2].Z = zOrder; // Upper right verts[3].X = ownerRect.Right; verts[3].Y = ownerRect.Top; verts[3].Color = 0; verts[3].Tu1 = 1.0f; verts[3].Tv1 = 0.0f; verts[3].Z = zOrder; PrimitiveBuffer.SetPrimitiveBuffer(ref _primitiveBuffer, ref verts, PrimitiveType.TriangleFan); _frameSize = skinNeutralAR ? ImageContext.AdjustForSkinAR(ownerRect.Size) : ownerRect.Size; _imageContext.FrameSize = _frameSize; }
protected override SizeF CalculateInnerDesiredSize(SizeF totalSize) { // Calculate constraints SizeF result = new SizeF(totalSize.Width, totalSize.Height); float desiredWidthFromHeight = result.Height * AspectRatio; if (result.Width < desiredWidthFromHeight) // Adapt height result.Height = result.Width / AspectRatio; else // Adapt width result.Width = desiredWidthFromHeight; return result; }
protected override void ArrangeChildren() { bool fireScrolled = false; lock (Children.SyncRoot) { _arrangedItemsStartIndex = -1; IItemProvider itemProvider = ItemProvider; if (itemProvider == null) { base.ArrangeChildren(); return; } _totalHeight = 0; _totalWidth = 0; int numItems = itemProvider.NumItems; if (numItems > 0) { PointF actualPosition = ActualPosition; SizeF actualSize = new SizeF((float) ActualWidth, (float) ActualHeight); // For Orientation == vertical, this is ActualHeight, for horizontal it is ActualWidth float actualExtendsInOrientationDirection = GetExtendsInOrientationDirection(Orientation, actualSize); // For Orientation == vertical, this is ActualWidth, for horizontal it is ActualHeight float actualExtendsInNonOrientationDirection = GetExtendsInNonOrientationDirection(Orientation, actualSize); // If set to true, we'll check available space from the last to first visible child. // That is necessary if we want to scroll a specific child to the last visible position. bool invertLayouting = false; //Percentage of child size to offset child positions float physicalOffset = _actualPhysicalOffset; if (_pendingScrollIndex.HasValue) { fireScrolled = true; int pendingSI = _pendingScrollIndex.Value; physicalOffset = _actualPhysicalOffset = _pendingPhysicalOffset; CalcHelper.Bound(ref pendingSI, 0, numItems - 1); if (_scrollToFirst) _actualFirstVisibleChildIndex = pendingSI; else { _actualLastVisibleChildIndex = pendingSI; //If we have an offset then there will be part of an additional item visible if (physicalOffset != 0) _actualLastVisibleChildIndex++; invertLayouting = true; } _pendingScrollIndex = null; } // 1) Calculate scroll indices if (_doScroll) { float spaceLeft = actualExtendsInOrientationDirection; //Allow space for partially visible items at top and bottom if (physicalOffset != 0) spaceLeft += _averageItemSize; if (invertLayouting) { CalcHelper.Bound(ref _actualLastVisibleChildIndex, 0, numItems - 1); _actualFirstVisibleChildIndex = _actualLastVisibleChildIndex + 1; int ct = MAX_NUM_VISIBLE_ITEMS; while (_actualFirstVisibleChildIndex > 0) { FrameworkElement item = GetItem(_actualFirstVisibleChildIndex - 1, itemProvider, true); if (item == null || !item.IsVisible) continue; if (ct-- == 0) break; spaceLeft -= GetExtendsInOrientationDirection(Orientation, item.DesiredSize); if (spaceLeft + DELTA_DOUBLE < 0) break; // Found item which is not visible any more _actualFirstVisibleChildIndex--; } if (_actualFirstVisibleChildIndex > _actualLastVisibleChildIndex) // Happens if the item at _actualFirstVisibleChildIndex is bigger than the available space _actualFirstVisibleChildIndex = _actualLastVisibleChildIndex; if (spaceLeft > 0) { // Correct the last scroll index to fill the available space while (_actualLastVisibleChildIndex < numItems - 1) { FrameworkElement item = GetItem(_actualLastVisibleChildIndex + 1, itemProvider, true); if (item == null || !item.IsVisible) continue; if (ct-- == 0) break; spaceLeft -= GetExtendsInOrientationDirection(Orientation, item.DesiredSize); if (spaceLeft + DELTA_DOUBLE < 0) break; // Found item which is not visible any more _actualLastVisibleChildIndex++; } } } else { CalcHelper.Bound(ref _actualFirstVisibleChildIndex, 0, numItems - 1); _actualLastVisibleChildIndex = _actualFirstVisibleChildIndex - 1; int ct = MAX_NUM_VISIBLE_ITEMS; while (_actualLastVisibleChildIndex < numItems - 1) { FrameworkElement item = GetItem(_actualLastVisibleChildIndex + 1, itemProvider, true); if (item == null || !item.IsVisible) continue; if (ct-- == 0) break; spaceLeft -= GetExtendsInOrientationDirection(Orientation, item.DesiredSize); if (spaceLeft + DELTA_DOUBLE < 0) break; // Found item which is not visible any more _actualLastVisibleChildIndex++; } if (_actualLastVisibleChildIndex < _actualFirstVisibleChildIndex) // Happens if the item at _actualFirstVisibleChildIndex is bigger than the available space _actualLastVisibleChildIndex = _actualFirstVisibleChildIndex; if (spaceLeft > 0) { // Correct the first scroll index to fill the available space while (_actualFirstVisibleChildIndex > 0) { FrameworkElement item = GetItem(_actualFirstVisibleChildIndex - 1, itemProvider, true); if (item == null || !item.IsVisible) continue; if (ct-- == 0) break; spaceLeft -= GetExtendsInOrientationDirection(Orientation, item.DesiredSize); if (spaceLeft + DELTA_DOUBLE < 0) break; // Found item which is not visible any more _actualFirstVisibleChildIndex--; } } } } else { _actualFirstVisibleChildIndex = 0; _actualLastVisibleChildIndex = numItems - 1; } // 2) Arrange children if (Orientation == Orientation.Vertical) _totalWidth = actualExtendsInNonOrientationDirection; else _totalHeight = actualExtendsInNonOrientationDirection; _arrangedItems.Clear(); _arrangedItemsStartIndex = _actualFirstVisibleChildIndex; // Heavy scrolling works best with at least two times the number of visible items arranged above and below // our visible children. That was tested out. If someone has a better heuristic, please use it here. int numArrangeAroundViewport = ((int) (actualExtendsInOrientationDirection / _averageItemSize) + 1) * NUM_ADD_MORE_FOCUS_ELEMENTS; // Elements before _actualFirstVisibleChildIndex //Calculate number of pixels to shift items up/left by based on offset float startOffset = -(physicalOffset * _averageItemSize); for (int i = _actualFirstVisibleChildIndex - 1; i >= 0 && i >= _actualFirstVisibleChildIndex - numArrangeAroundViewport; i--) { FrameworkElement item = GetItem(i, itemProvider, true); if (item == null || !item.IsVisible) continue; SizeF childSize = item.DesiredSize; // For Orientation == vertical, this is childSize.Height, for horizontal it is childSize.Width float desiredExtendsInOrientationDirection = GetExtendsInOrientationDirection(Orientation, childSize); startOffset -= desiredExtendsInOrientationDirection; if (Orientation == Orientation.Vertical) { PointF position = new PointF(actualPosition.X, actualPosition.Y + startOffset); childSize.Width = actualExtendsInNonOrientationDirection; ArrangeChildHorizontal(item, item.HorizontalAlignment, ref position, ref childSize); item.Arrange(SharpDXExtensions.CreateRectangleF(position, childSize)); _totalHeight += desiredExtendsInOrientationDirection; } else { PointF position = new PointF(actualPosition.X + startOffset, actualPosition.Y); childSize.Height = actualExtendsInNonOrientationDirection; ArrangeChildVertical(item, item.VerticalAlignment, ref position, ref childSize); item.Arrange(SharpDXExtensions.CreateRectangleF(position, childSize)); _totalWidth += desiredExtendsInOrientationDirection; } _arrangedItems.Insert(0, item); _arrangedItemsStartIndex = i; } //Calculate number of pixels to shift items up/left by based on offset startOffset = -(physicalOffset * _averageItemSize); // Elements from _actualFirstVisibleChildIndex to _actualLastVisibleChildIndex + _numArrangeAroundViewport for (int i = _actualFirstVisibleChildIndex; i < numItems && i <= _actualLastVisibleChildIndex + numArrangeAroundViewport; i++) { FrameworkElement item = GetItem(i, itemProvider, true); if (item == null || !item.IsVisible) continue; SizeF childSize = item.DesiredSize; // For Orientation == vertical, this is childSize.Height, for horizontal it is childSize.Width float desiredExtendsInOrientationDirection = GetExtendsInOrientationDirection(Orientation, childSize); if (Orientation == Orientation.Vertical) { PointF position = new PointF(actualPosition.X, actualPosition.Y + startOffset); childSize.Width = actualExtendsInNonOrientationDirection; ArrangeChildHorizontal(item, item.HorizontalAlignment, ref position, ref childSize); item.Arrange(SharpDXExtensions.CreateRectangleF(position, childSize)); _totalHeight += desiredExtendsInOrientationDirection; startOffset += desiredExtendsInOrientationDirection; } else { PointF position = new PointF(actualPosition.X + startOffset, actualPosition.Y); childSize.Height = actualExtendsInNonOrientationDirection; ArrangeChildVertical(item, item.VerticalAlignment, ref position, ref childSize); item.Arrange(SharpDXExtensions.CreateRectangleF(position, childSize)); _totalWidth += desiredExtendsInOrientationDirection; startOffset += desiredExtendsInOrientationDirection; } _arrangedItems.Add(item); } int numInvisible = numItems - _arrangedItems.Count; // Items which have not been arranged above, i.e. item extends have not been added to _totalHeight / _totalWidth float invisibleRequiredSize = numInvisible * _averageItemSize; if (_doScroll) invisibleRequiredSize += actualExtendsInOrientationDirection % _averageItemSize; // Size gap from the last item to the end of the actual extends if (Orientation == Orientation.Vertical) _totalHeight += invisibleRequiredSize; else _totalWidth += invisibleRequiredSize; itemProvider.Keep(_arrangedItemsStartIndex - INVISIBLE_KEEP_THRESHOLD, _arrangedItemsStartIndex + _arrangedItems.Count + INVISIBLE_KEEP_THRESHOLD); } else { _arrangedItemsStartIndex = 0; _actualFirstVisibleChildIndex = 0; _actualLastVisibleChildIndex = -1; } } if (fireScrolled) InvokeScrolled(); }
/// <summary> /// Returns the information whether the image with the given <paramref name="imageSize"/> has landscape orientation /// in relation to the given <paramref name="outputSize"/>. A landscape image has borders at its top and bottom while /// a portrait image has borders at its left and right sides. /// </summary> /// <param name="imageSize">Size or aspect ratio of the image.</param> /// <param name="outputSize">Size or aspect ratio of the available output region to show the image.</param> /// <returns><c>true</c>, if the image has landscape orientation, else <c>false</c>.</returns> protected bool IsLandscape(SizeF imageSize, SizeF outputSize) { return imageSize.Width / outputSize.Width > imageSize.Height / outputSize.Height; }
protected override SizeF CalculateInnerDesiredSize(SizeF totalSize) { FrameworkElementCollection children = Children; lock (children.SyncRoot) { if (_newItemProvider != null) { if (children.Count > 0) children.Clear(false); if (_itemProvider != null) MPF.TryCleanupAndDispose(_itemProvider); _itemProvider = _newItemProvider; _newItemProvider = null; _updateRenderOrder = true; } _averageItemSize = 0; IItemProvider itemProvider = ItemProvider; if (itemProvider == null) return base.CalculateInnerDesiredSize(totalSize); int numItems = itemProvider.NumItems; if (numItems == 0) return new SizeF(); SizeF resultSize; // Get all viewable children (= visible children inside our range) IList<FrameworkElement> exemplaryChildren = GetMeasuredViewableChildren(totalSize, out resultSize); if (exemplaryChildren.Count == 0) { // Might be the case if no item matches into totalSize. Fallback: Use the first visible item. for (int i = 0; i < numItems; i++) { FrameworkElement item = GetItem(i, itemProvider, true); if (item == null || !item.IsVisible) continue; exemplaryChildren.Add(item); } } if (exemplaryChildren.Count == 0) return new SizeF(); _averageItemSize = GetExtendsInOrientationDirection(Orientation, resultSize) / exemplaryChildren.Count; return Orientation == Orientation.Vertical ? new SizeF(resultSize.Width, resultSize.Height * numItems / exemplaryChildren.Count) : new SizeF(resultSize.Width * numItems / exemplaryChildren.Count, resultSize.Height); } }
// It's actually "GetVisibleChildren", but that member already exists in Panel protected IList<FrameworkElement> GetMeasuredViewableChildren(SizeF totalSize, out SizeF resultSize) { resultSize = new SizeF(); IList<FrameworkElement> result = new List<FrameworkElement>(20); IItemProvider itemProvider = ItemProvider; if (itemProvider == null) return result; int numItems = itemProvider.NumItems; if (numItems == 0) return result; float availableSize = GetExtendsInNonOrientationDirection(Orientation, totalSize); if (!_doScroll) _actualFirstVisibleChildIndex = 0; int start = _actualFirstVisibleChildIndex; CalcHelper.Bound(ref start, 0, numItems - 1); int end = start - 1; float sumExtendsInOrientationDirection = 0; float maxExtendsInNonOrientationDirection = 0; int ct = MAX_NUM_VISIBLE_ITEMS; // From scroll index until potentially up to the end do { if (end == numItems - 1) // Reached the last item break; FrameworkElement item = GetItem(end + 1, itemProvider, true); if (item == null || !item.IsVisible) { end++; continue; } if (ct-- == 0) break; float childExtendsInOrientationDirection = GetExtendsInOrientationDirection(Orientation, item.DesiredSize); if (childExtendsInOrientationDirection > availableSize + DELTA_DOUBLE) break; float childExtendsInNonOrientationDirection = GetExtendsInNonOrientationDirection(Orientation, item.DesiredSize); availableSize -= childExtendsInOrientationDirection; sumExtendsInOrientationDirection += childExtendsInOrientationDirection; if (childExtendsInNonOrientationDirection > maxExtendsInNonOrientationDirection) maxExtendsInNonOrientationDirection = childExtendsInNonOrientationDirection; result.Add(item); end++; } while (availableSize > 0 || !_doScroll); // If there is still space left, try to get items above scroll index while (availableSize > 0) { if (start == 0) // Reached the last item break; FrameworkElement item = GetItem(start - 1, itemProvider, true); if (item == null || !item.IsVisible) continue; if (ct-- == 0) break; float childExtendsInOrientationDirection = GetExtendsInOrientationDirection(Orientation, item.DesiredSize); if (childExtendsInOrientationDirection > availableSize + DELTA_DOUBLE) break; float childExtendsInNonOrientationDirection = GetExtendsInNonOrientationDirection(Orientation, item.DesiredSize); availableSize -= childExtendsInOrientationDirection; sumExtendsInOrientationDirection += childExtendsInOrientationDirection; if (childExtendsInNonOrientationDirection > maxExtendsInNonOrientationDirection) maxExtendsInNonOrientationDirection = childExtendsInNonOrientationDirection; result.Insert(0, item); start--; } resultSize = Orientation == Orientation.Vertical ? new SizeF(maxExtendsInNonOrientationDirection, sumExtendsInOrientationDirection) : new SizeF(sumExtendsInOrientationDirection, maxExtendsInNonOrientationDirection); return result; }
protected override SizeF CalculateInnerDesiredSize(SizeF totalSize) { return CalculateDesiredSize(GetVisibleChildren().GetEnumerator(), totalSize); }
protected override SizeF CalculateInnerDesiredSize(SizeF totalSize) { base.CalculateInnerDesiredSize(totalSize); // Needs to be called in each sub class of Control, see comment in Control.CalculateInnerDesiredSize() AllocFont(); SizeF childSize = _asset == null ? new SizeF() : new SizeF(_asset.TextWidth(VisibleText ?? string.Empty), _asset.TextHeight(1)); if (PreferredTextLength.HasValue && _asset != null) // We use the "W" character as the character which needs the most space in X-direction childSize.Width = PreferredTextLength.Value * _asset.TextWidth("W"); return childSize; }
protected SizeF MaxSizeF(SizeF a, SizeF b) { return new SizeF(Math.Max(a.Width, b.Width), Math.Max(a.Height, b.Height)); }
/// <summary> /// Creates a rectangular <see cref="GraphicsPath"/> with rounded edges, optionally with an open title /// region specified by the parameters <paramref name="titleInset"/> and <paramref name="titleWidth"/>. /// </summary> /// <param name="baseRect">The rect which surrounds the created path.</param> /// <param name="radiusX">The X radius of the rounded edges.</param> /// <param name="radiusY">The Y radius of the rounded edges.</param> /// <param name="withTitleRegion">If set to <c>true</c>, a title region will be left out.</param> /// <param name="titleInset">Inset of the title region behind the corner. This parameter will only be used if /// <paramref name="withTitleRegion"/> is set to <c>true</c>.</param> /// <param name="titleWidth">Width of the title region to leave out. This parameter will only be used if /// <paramref name="withTitleRegion"/> is set to <c>true</c>.</param> public static GraphicsPath CreateRoundedRectWithTitleRegionPath(RectangleF baseRect, float radiusX, float radiusY, bool withTitleRegion, float titleInset, float titleWidth) { GraphicsPath result = new GraphicsPath(); if (radiusX <= 0.0f && radiusY <= 0.0f || baseRect.Width == 0 || baseRect.Height == 0) { // if corner radius is less than or equal to zero, return the original rectangle if (withTitleRegion) { // If we should leave out a title region, we need to do it manually, because we need to start next to the // title. titleWidth = Math.Min(titleWidth, baseRect.Width - 2 * titleInset); // Right from the title to the upper right edge result.AddLine(baseRect.Left + 2* titleInset + titleWidth, baseRect.Top, baseRect.Right, baseRect.Top); // Upper right edge to lower right edge result.AddLine(baseRect.Right, baseRect.Top, baseRect.Right, baseRect.Bottom); // Lower right edge to lower left edge result.AddLine(baseRect.Right, baseRect.Bottom, baseRect.Left, baseRect.Bottom); // Lower left edge to upper left edge result.AddLine(baseRect.Left, baseRect.Bottom, baseRect.Left, baseRect.Top); // Upper left edge to the left side of the title result.AddLine(baseRect.Left, baseRect.Top, baseRect.Left + titleInset, baseRect.Top); } else result.AddRectangle(baseRect.ToDrawingRectF()); } else { if (radiusX >= baseRect.Width / 2f) radiusX = baseRect.Width/2f; if (radiusY >= baseRect.Height / 2f) radiusY = baseRect.Height/2f; // create the arc for the rectangle sides and declare a graphics path object for the drawing SizeF sizeF = new SizeF(radiusX * 2f, radiusY * 2f); RectangleF arc = SharpDXExtensions.CreateRectangleF(baseRect.Location, sizeF); if (withTitleRegion) { titleWidth = Math.Min(titleWidth, baseRect.Width - 2 * (radiusX + titleInset)); // Right of the title to the upper right edge result.AddLine(baseRect.Left + radiusX + titleInset + titleWidth, baseRect.Top, baseRect.Right - radiusX, baseRect.Top); } // Top right arc arc.X = baseRect.Right - radiusX * 2f; result.AddArc(arc.ToDrawingRectF(), 270, 90); // Bottom right arc arc.Y = baseRect.Bottom - radiusY * 2f; result.AddArc(arc.ToDrawingRectF(), 0, 90); // Bottom left arc arc.X = baseRect.Left; result.AddArc(arc.ToDrawingRectF(), 90, 90); // Top left arc arc.Y = baseRect.Top; result.AddArc(arc.ToDrawingRectF(), 180, 90); if (withTitleRegion) // Upper left edge to the left side of the title result.AddLine(baseRect.Left + radiusX, baseRect.Top, baseRect.Left + radiusX + titleInset, baseRect.Top); else result.CloseFigure(); } result.Flatten(); return result; }
/// <summary> /// Measures this element's size and fills the <see cref="DesiredSize"/> property. /// </summary> /// <remarks> /// <para> /// This method is the first part of the two-phase measuring process. In this first phase, parent /// controls collect all the size requirements of their child controls. /// </para> /// <para> /// An input size value of <see cref="float.NaN"/> in any coordinate denotes that this child control doesn't have a size /// constraint in that direction. Coordinates different from <see cref="float.NaN"/> should be considered by this child /// control as the maximum available size in that direction. If this element still produces a bigger /// <see cref="DesiredSize"/>, the <see cref="Arrange(RectangleF)"/> method might give it a smaller final region. /// </para> /// </remarks> /// <param name="totalSize">Total size of the element including Margins. As input, this parameter /// contains the size available for this child control (size constraint). As output, it must be set /// to the <see cref="DesiredSize"/> plus <see cref="UIElement.Margin"/>.</param> public void Measure(ref SizeF totalSize) { #if DEBUG_LAYOUT #if DEBUG_MORE_LAYOUT System.Diagnostics.Trace.WriteLine(string.Format("Measure {0} Name='{1}', totalSize={2}", GetType().Name, Name, totalSize)); #endif #endif if (!_isMeasureInvalid && SameSize(_availableSize, totalSize)) { // Optimization: If our input data is the same and the layout isn't invalid, we don't need to measure again totalSize = _desiredSize; #if DEBUG_LAYOUT #if DEBUG_MORE_LAYOUT System.Diagnostics.Trace.WriteLine(string.Format("Measure {0} Name='{1}', cutting short, totalSize is like before and measurement is not invalid, returns desired size={2}", GetType().Name, Name, totalSize)); #endif #endif return; } #if DEBUG_LAYOUT #if !DEBUG_MORE_LAYOUT System.Diagnostics.Trace.WriteLine(string.Format("Measure {0} Name='{1}', totalSize={2}", GetType().Name, Name, totalSize)); #endif #endif _isMeasureInvalid = false; _availableSize = totalSize; RemoveMargin(ref totalSize, Margin); Matrix? layoutTransform = LayoutTransform == null ? new Matrix?() : LayoutTransform.GetTransform(); if (layoutTransform.HasValue) totalSize = FindMaxTransformedSize(layoutTransform.Value, totalSize); if (!double.IsNaN(Width)) totalSize.Width = (float) Width; if (!double.IsNaN(Height)) totalSize.Height = (float) Height; totalSize = CalculateInnerDesiredSize(totalSize); if (!double.IsNaN(Width)) totalSize.Width = (float) Width; if (!double.IsNaN(Height)) totalSize.Height = (float) Height; totalSize = ClampSize(totalSize); _innerDesiredSize = totalSize; if (layoutTransform.HasValue) layoutTransform.Value.TransformIncludingRectangleSize(ref totalSize); AddMargin(ref totalSize, Margin); if (totalSize != _desiredSize) InvalidateLayout(false, true); _desiredSize = totalSize; #if DEBUG_LAYOUT System.Diagnostics.Trace.WriteLine(string.Format("Measure {0} Name='{1}', returns calculated desired size={2}", GetType().Name, Name, totalSize)); #endif }
public void Update(SizeF targetImageSize, Texture texture, RectangleF textureClip) { RefreshParameters(targetImageSize, texture, textureClip); }
/// <summary> /// Given the transform to be applied to an unknown rectangle, this method finds (in axis-aligned local space) /// the largest rectangle that, after transform, fits within <paramref name="localBounds"/>. /// Largest rectangle means rectangle of the greatest area in local space (although maximal area in local space /// implies maximal area in transform space). /// </summary> /// <param name="transform">Transformation matrix.</param> /// <param name="localBounds">The bounds in local space where the returned size fits when transformed /// via the given <paramref name="transform"/>.</param> /// <returns>The dimensions, in local space, of the maximal area rectangle found.</returns> private static SizeF FindMaxTransformedSize(Matrix transform, SizeF localBounds) { // X (width) and Y (height) constraints for axis-aligned bounding box in dest. space float xConstr = localBounds.Width; float yConstr = localBounds.Height; // Avoid doing math on an empty rect if (IsNear(xConstr, 0) || IsNear(yConstr, 0)) return new SizeF(0, 0); bool xConstrInfinite = float.IsNaN(xConstr); bool yConstrInfinite = float.IsNaN(yConstr); if (xConstrInfinite && yConstrInfinite) return new SizeF(float.NaN, float.NaN); if (xConstrInfinite) // Assume square for one-dimensional constraint xConstr = yConstr; else if (yConstrInfinite) yConstr = xConstr; // We only deal with nonsingular matrices here. The nonsingular matrix is the one // that has inverse (determinant != 0). if (transform.Determinant() == 0) return new SizeF(0, 0); float a = transform.M11; float b = transform.M12; float c = transform.M21; float d = transform.M22; // Result width and height (in child/local space) float w; float h; // Because we are dealing with nonsingular transform matrices, we have (b==0 || c==0) XOR (a==0 || d==0) if (IsNear(b, 0) || IsNear(c, 0)) { // (b == 0 || c == 0) ==> a != 0 && d != 0 float yCoverD = yConstrInfinite ? float.PositiveInfinity : Math.Abs(yConstr / d); float xCoverA = xConstrInfinite ? float.PositiveInfinity : Math.Abs(xConstr / a); if (IsNear(b, 0)) { if (IsNear(c, 0)) { // b == 0, c == 0, a != 0, d != 0 // No constraint relation; use maximal width and height h = yCoverD; w = xCoverA; } else { // b == 0, a != 0, c != 0, d != 0 // Maximizing under line (hIntercept=xConstr/c, wIntercept=xConstr/a) // BUT we still have constraint: h <= yConstr/d h = Math.Min(0.5f * Math.Abs(xConstr / c), yCoverD); w = xCoverA - ((c * h) / a); } } else { // c == 0, a != 0, b != 0, d != 0 // Maximizing under line (hIntercept=yConstr/d, wIntercept=yConstr/b) // BUT we still have constraint: w <= xConstr/a w = Math.Min(0.5f * Math.Abs(yConstr / b), xCoverA); h = yCoverD - ((b * w) / d); } } else if (IsNear(a, 0) || IsNear(d, 0)) { // (a == 0 || d == 0) ==> b != 0 && c != 0 float yCoverB = Math.Abs(yConstr / b); float xCoverC = Math.Abs(xConstr / c); if (IsNear(a, 0)) { if (IsNear(d, 0)) { // a == 0, d == 0, b != 0, c != 0 // No constraint relation; use maximal width and height h = xCoverC; w = yCoverB; } else { // a == 0, b != 0, c != 0, d != 0 // Maximizing under line (hIntercept=yConstr/d, wIntercept=yConstr/b) // BUT we still have constraint: h <= xConstr/c h = Math.Min(0.5f * Math.Abs(yConstr / d), xCoverC); w = yCoverB - ((d * h) / b); } } else { // d == 0, a != 0, b != 0, c != 0 // Maximizing under line (hIntercept=xConstr/c, wIntercept=xConstr/a) // BUT we still have constraint: w <= yConstr/b w = Math.Min(0.5f * Math.Abs(xConstr / a), yCoverB); h = xCoverC - ((a * w) / c); } } else { float xCoverA = Math.Abs(xConstr / a); // w-intercept of x-constraint line float xCoverC = Math.Abs(xConstr / c); // h-intercept of x-constraint line float yCoverB = Math.Abs(yConstr / b); // w-intercept of y-constraint line float yCoverD = Math.Abs(yConstr / d); // h-intercept of y-constraint line // The tighest constraint governs, so we pick the lowest constraint line // The optimal point (w, h) for which Area = w*h is maximized occurs halfway to each intercept. w = Math.Min(yCoverB, xCoverA) * 0.5f; h = Math.Min(xCoverC, yCoverD) * 0.5f; if ((GreaterThanOrClose(xCoverA, yCoverB) && LessThanOrClose(xCoverC, yCoverD)) || (LessThanOrClose(xCoverA, yCoverB) && GreaterThanOrClose(xCoverC, yCoverD))) { // Constraint lines cross; since the most restrictive constraint wins, // we have to maximize under two line segments, which together are discontinuous. // Instead, we maximize w*h under the line segment from the two smallest endpoints. // Since we are not (except for in corner cases) on the original constraint lines, // we are not using up all the available area in transform space. So scale our shape up // until it does in at least one dimension. SizeF childSizeTr = new SizeF(w, h); transform.TransformIncludingRectangleSize(ref childSizeTr); float expandFactor = Math.Min(xConstr / childSizeTr.Width, yConstr / childSizeTr.Height); if (!float.IsNaN(expandFactor) && !float.IsInfinity(expandFactor)) { w *= expandFactor; h *= expandFactor; } } } return new SizeF(w, h); }
protected virtual SizeF CalculateInnerDesiredSize(SizeF totalSize) { return new SizeF(); }
/// <summary> /// Updates tle layout of this element in the render thread. /// In this method, <see cref="Measure(ref SizeF)"/> and <see cref="Arrange(RectangleF)"/> are called. /// </summary> /// <remarks> /// This method should actually be located in the <see cref="Screen"/> class but I leave it here because all the /// layout debug defines are in the scope of this file. /// This method must be called from the render thread before the call to <see cref="Render"/>. /// </remarks> /// <param name="skinSize">The size of the skin.</param> public void UpdateLayoutRoot(SizeF skinSize) { SizeF size = skinSize; #if DEBUG_LAYOUT #if DEBUG_MORE_LAYOUT System.Diagnostics.Trace.WriteLine(string.Format("UpdateLayoutRoot {0} Name='{1}', measuring with screen size {2}", GetType().Name, Name, skinSize)); #endif #endif do { Measure(ref size); } while (_isMeasureInvalid); #if DEBUG_LAYOUT #if DEBUG_MORE_LAYOUT System.Diagnostics.Trace.WriteLine(string.Format("UpdateLayout {0} Name='{1}', arranging with screen size {2}", GetType().Name, Name, skinSize)); #endif #endif // Ignore the measured size - arrange with screen size Arrange(SharpDXExtensions.CreateRectangleF(new PointF(0, 0), skinSize)); }
protected static SizeF CalculateDesiredSize(IEnumerator<FrameworkElement> currentVisibleChildEnumerator, SizeF currentAvailableSize) { if (!currentVisibleChildEnumerator.MoveNext()) return new SizeF(0, 0); FrameworkElement child = currentVisibleChildEnumerator.Current; if (child == null) // Not necessary to check this, only to avoid warning return new SizeF(); SizeF childSize = new SizeF(currentAvailableSize.Width, currentAvailableSize.Height); SizeF nextChildrenDesiredSize; Dock childDock = GetDock(child); if (childDock == Dock.Top || childDock == Dock.Bottom) { child.Measure(ref childSize); currentAvailableSize.Height -= childSize.Height; nextChildrenDesiredSize = CalculateDesiredSize(currentVisibleChildEnumerator, currentAvailableSize); return new SizeF(Math.Max(childSize.Width, nextChildrenDesiredSize.Width), childSize.Height + nextChildrenDesiredSize.Height); } if (childDock == Dock.Left || childDock == Dock.Right) { child.Measure(ref childSize); currentAvailableSize.Width -= childSize.Width; nextChildrenDesiredSize = CalculateDesiredSize(currentVisibleChildEnumerator, currentAvailableSize); return new SizeF(childSize.Width + nextChildrenDesiredSize.Width, Math.Max(childSize.Height, nextChildrenDesiredSize.Height)); } // Else assume center child.Measure(ref childSize); nextChildrenDesiredSize = CalculateDesiredSize(currentVisibleChildEnumerator, currentAvailableSize); return new SizeF(Math.Max(childSize.Width, nextChildrenDesiredSize.Width), Math.Max(childSize.Height, nextChildrenDesiredSize.Height)); }
protected SizeF ClampSize(SizeF size) { if (!float.IsNaN(size.Width)) size.Width = (float) Math.Min(Math.Max(size.Width, MinWidth), MaxWidth); if (!float.IsNaN(size.Height)) size.Height = (float) Math.Min(Math.Max(size.Height, MinHeight), MaxHeight); return size; }
protected override void ArrangeOverride() { base.ArrangeOverride(); float offsetTop = 0.0f; float offsetLeft = 0.0f; float offsetRight = 0.0f; float offsetBottom = 0.0f; SizeF availableSize = new SizeF(_innerRect.Width, _innerRect.Height); int count = 0; // Area allocated to child SizeF childArea; IList<FrameworkElement> visibleChildren = GetVisibleChildren(); foreach (FrameworkElement child in visibleChildren) { count++; //Trace.WriteLine(String.Format("DockPanel:arrange {0} {1}", count, child.Name)); // Size of the child SizeF childSize = child.DesiredSize; switch (GetDock(child)) { case Dock.Top: { PointF location = new PointF(offsetLeft, offsetTop); location.X += ActualPosition.X; location.Y += ActualPosition.Y; // Allocate area to child if (count == visibleChildren.Count && LastChildFill) childArea = new SizeF(availableSize.Width, availableSize.Height); else childArea = new SizeF(availableSize.Width, childSize.Height); // Position the child within the child area ArrangeChildHorizontal(child, child.HorizontalAlignment, ref location, ref childArea); child.Arrange(SharpDXExtensions.CreateRectangleF(location, childArea)); offsetTop += childArea.Height; availableSize.Height -= childArea.Height; } break; case Dock.Bottom: { PointF location; if (count == visibleChildren.Count && LastChildFill) location = new PointF(offsetLeft, _innerRect.Height - (offsetBottom + availableSize.Height)); else location = new PointF(offsetLeft, _innerRect.Height - (offsetBottom + childSize.Height)); location.X += ActualPosition.X; location.Y += ActualPosition.Y; // Allocate area to child if (count == visibleChildren.Count && LastChildFill) childArea = new SizeF(availableSize.Width, availableSize.Height); else childArea = new SizeF(availableSize.Width, childSize.Height); // Position the child within the child area ArrangeChildHorizontal(child, child.HorizontalAlignment, ref location, ref childArea); child.Arrange(SharpDXExtensions.CreateRectangleF(location, childArea)); offsetBottom += childArea.Height; availableSize.Height -= childArea.Height; } break; case Dock.Left: { PointF location = new PointF(offsetLeft, offsetTop); location.X += ActualPosition.X; location.Y += ActualPosition.Y; // Allocate area to child if (count == visibleChildren.Count && LastChildFill) childArea = new SizeF(availableSize.Width, availableSize.Height); else childArea = new SizeF(childSize.Width, availableSize.Height); // Position the child within the child area ArrangeChildVertical(child, child.VerticalAlignment, ref location, ref childArea); child.Arrange(SharpDXExtensions.CreateRectangleF(location, childArea)); offsetLeft += childArea.Width; availableSize.Width -= childArea.Width; } break; case Dock.Right: { PointF location; if (count == visibleChildren.Count && LastChildFill) location = new PointF(_innerRect.Width - (offsetRight + availableSize.Width), offsetTop); else location = new PointF(_innerRect.Width - (offsetRight + childSize.Width), offsetTop); location.X += ActualPosition.X; location.Y += ActualPosition.Y; // Allocate area to child if (count == visibleChildren.Count && LastChildFill) childArea = new SizeF(availableSize.Width,availableSize.Height); else childArea = new SizeF(childSize.Width,availableSize.Height); // Position the child within the child area ArrangeChildVertical(child, child.VerticalAlignment, ref location, ref childArea); child.Arrange(SharpDXExtensions.CreateRectangleF(location, childArea)); offsetRight += childArea.Width; availableSize.Width -= childArea.Width; } break; default: // Dock.Center { PointF location = new PointF(offsetLeft, offsetTop); location.X += ActualPosition.X; location.Y += ActualPosition.Y; childSize = new SizeF(availableSize.Width, availableSize.Height); if (count == visibleChildren.Count && LastChildFill) child.Arrange(SharpDXExtensions.CreateRectangleF(location, childSize)); else { ArrangeChild(child, child.HorizontalAlignment, child.VerticalAlignment, ref location, ref childSize); child.Arrange(SharpDXExtensions.CreateRectangleF(location, childSize)); } // Do not remove child size from a border offset or from size - the child will // stay in the "empty space" without taking place from the border layouting variables } break; } } }
/// <summary> /// Arranges the child horizontal and vertical in a given area. If the area is bigger than /// the child's desired size, the child will be arranged according to the given <paramref name="horizontalAlignment"/> /// and <paramref name="verticalAlignment"/>. /// </summary> /// <param name="child">The child to arrange. The child will not be changed by this method.</param> /// <param name="horizontalAlignment">Alignment in horizontal direction.</param> /// <param name="verticalAlignment">Alignment in vertical direction.</param> /// <param name="location">Input: The starting position of the available area. Output: The position /// the child should be located.</param> /// <param name="childSize">Input: The available area for the <paramref name="child"/>. Output: /// The area the child should take.</param> public void ArrangeChild(FrameworkElement child, HorizontalAlignmentEnum horizontalAlignment, VerticalAlignmentEnum verticalAlignment, ref PointF location, ref SizeF childSize) { // Be careful when changing the implementation of those arrangement methods. // MPF behaves a bit different from WPF: We don't clip elements at the boundaries of containers, // instead, we arrange them with a maximum size calculated by the container. If we would not avoid // that controls can become bigger than their arrange size, we would have to accomplish a means to clip // their render size. ArrangeChildHorizontal(child, horizontalAlignment, ref location, ref childSize); ArrangeChildVertical(child, verticalAlignment, ref location, ref childSize); }
protected override SizeF CalculateInnerDesiredSize(SizeF totalSize) { // Return the biggest available child extents SizeF childSize; SizeF maxChildSize = new SizeF(float.NaN, float.NaN); foreach (FrameworkElement child in GetVisibleChildren()) { childSize = new SizeF(totalSize.Width, totalSize.Height); child.Measure(ref childSize); if (float.IsNaN(maxChildSize.Width) || childSize.Width > maxChildSize.Width) maxChildSize.Width = childSize.Width; if (float.IsNaN(maxChildSize.Height) || childSize.Height > maxChildSize.Height) maxChildSize.Height = childSize.Height; } if (_ellipsisControl == null) _ellipsisControl = CreateEllipsisControl(); childSize = new SizeF(totalSize.Width, totalSize.Height); _ellipsisControl.Measure(ref childSize); if (float.IsNaN(maxChildSize.Width) || childSize.Width > maxChildSize.Width) maxChildSize.Width = childSize.Width; if (float.IsNaN(maxChildSize.Height) || childSize.Height > maxChildSize.Height) maxChildSize.Height = childSize.Height; return maxChildSize; }
/// <summary> /// Arranges the child horizontal in a given area. If the area is bigger than the child's desired /// size, the child will be arranged according to the given <paramref name="alignment"/>. /// </summary> /// <param name="child">The child to arrange. The child will not be changed by this method.</param> /// <param name="alignment">Alignment in horizontal direction.</param> /// <param name="location">Input: The starting position of the available area. Output: The position /// the child should be located.</param> /// <param name="childSize">Input: The available area for the <paramref name="child"/>. Output: /// The area the child should take.</param> public void ArrangeChildHorizontal(FrameworkElement child, HorizontalAlignmentEnum alignment, ref PointF location, ref SizeF childSize) { // See comment in ArrangeChild SizeF desiredSize = child.DesiredSize; if (!double.IsNaN(desiredSize.Width) && desiredSize.Width <= childSize.Width) { // Width takes precedence over Stretch - Use Center as fallback if (alignment == HorizontalAlignmentEnum.Center || (alignment == HorizontalAlignmentEnum.Stretch && !double.IsNaN(child.Width))) { location.X += (childSize.Width - desiredSize.Width) / 2; childSize.Width = desiredSize.Width; } if (alignment == HorizontalAlignmentEnum.Right) { location.X += childSize.Width - desiredSize.Width; childSize.Width = desiredSize.Width; } else if (alignment == HorizontalAlignmentEnum.Left) { // Leave location unchanged childSize.Width = desiredSize.Width; } //else if (child.HorizontalAlignment == HorizontalAlignmentEnum.Stretch) // - Use all the space, nothing to do here } }