protected override bool TryCalculateStateName(LayoutState layout, out string stateName) { // Only continue if there are rules if ((Rules != null) && (Rules.Count > 0)) { // Try to find the first matching rule var rule = Rules.Where((r) => r.Applies(layout)).FirstOrDefault(); // If found, make sure state name is valid and set it if (rule != null) { if (string.IsNullOrEmpty(rule.StateName)) { throw new InvalidOperationException("The matching rule did not provide a valid state name."); } // Return the state stateName = rule.StateName; // Success return(true); } } // No matching rule found stateName = null; return(false); }
/// <summary> /// Updates or creates a sublayer to render a border-like shape. /// </summary> /// <param name="owner">The parent layer to apply the shape</param> /// <param name="area">The rendering area</param> /// <param name="background">The background brush</param> /// <param name="borderThickness">The border thickness</param> /// <param name="borderBrush">The border brush</param> /// <param name="cornerRadius">The corner radius</param> /// <param name="backgroundImage">The background image in case of a ImageBrush background</param> public void UpdateLayer( UIView owner, Brush background, Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius, UIImage backgroundImage ) { // Frame is captured to avoid calling twice calls below. var frame = owner.Frame; var area = new CGRect(0, 0, frame.Width, frame.Height); var newState = new LayoutState(area, background, borderThickness, borderBrush, cornerRadius, backgroundImage); var previousLayoutState = _currentState; if (!newState.Equals(previousLayoutState)) { if ( background != null || (borderThickness != Thickness.Empty && borderBrush != null) ) { _layerDisposable.Disposable = null; _layerDisposable.Disposable = InnerCreateLayer(owner.Layer, area, background, borderThickness, borderBrush, cornerRadius); } else { _layerDisposable.Disposable = null; } _currentState = newState; } }
private static void ShowButtons(SerializedProperty list, int index, LayoutListOptions options) { if (GUILayout.Button(moveButtonContent, EditorStyles.miniButtonLeft, miniButtonWidth)) { list.MoveArrayElement(index, index + 1); } if (GUILayout.Button(duplicateButtonContent, EditorStyles.miniButtonMid, miniButtonWidth)) { list.InsertArrayElementAtIndex(index); } if (GUILayout.Button(deleteButtonContent, EditorStyles.miniButtonRight, miniButtonWidth)) { list.DeleteArrayElementAtIndex(index); } GUI.backgroundColor = Color.green; if (GUILayout.Button(applyButtonContent, EditorStyles.miniButton, actionButtonWidth)) { LayoutState layoutState = list.serializedObject.targetObject as LayoutState; layoutState.layouts[index].Save(layoutState.objectsReference); } GUI.backgroundColor = Color.yellow; if (GUILayout.Button(showButtonContent, EditorStyles.miniButton, actionButtonWidth)) { LayoutState layoutState = list.serializedObject.targetObject as LayoutState; layoutState.layouts[index].Load(layoutState.objectsReference); } GUI.backgroundColor = Color.white; }
/// <summary> /// Updates or creates a sublayer to render a border-like shape. /// </summary> /// <param name="view">The view to which we should add the layers</param> /// <param name="background">The background brush of the border</param> /// <param name="borderThickness">The border thickness</param> /// <param name="borderBrush">The border brush</param> /// <param name="cornerRadius">The corner radius</param> /// <param name="padding">The padding to apply on the content</param> public void UpdateLayer( FrameworkElement view, Brush background, Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius, Thickness padding, bool willUpdateMeasures = false ) { // This is required because android Height and Width are hidden by Control. var baseView = view as View; var logicalDrawArea = view.LayoutSlot; // Set origin to 0, because drawArea should be in the coordinates of the view itself logicalDrawArea.X = 0; logicalDrawArea.Y = 0; var drawArea = logicalDrawArea.LogicalToPhysicalPixels(); var newState = new LayoutState(drawArea, background, borderThickness, borderBrush, cornerRadius, padding); var previousLayoutState = _currentState; if (!newState.Equals(previousLayoutState)) { bool imageHasChanged = newState.BackgroundImageSource != previousLayoutState?.BackgroundImageSource; bool shouldDisposeEagerly = imageHasChanged || newState.BackgroundImageSource == null; if (shouldDisposeEagerly) { // Clear previous value anyway in order to make sure the previous values are unset before the new ones. // This prevents the case where a second update would set a new background and then set the background to null when disposing the previous. _layerDisposable.Disposable = null; } Action onImageSet = null; var disposable = InnerCreateLayers(view, drawArea, background, borderThickness, borderBrush, cornerRadius, () => onImageSet?.Invoke()); // Most of the time we immediately dispose the previous layer. In the case where we're using an ImageBrush, // and the backing image hasn't changed, we dispose the previous layer at the moment the new background is applied, // to prevent a visible flicker. if (shouldDisposeEagerly) { _layerDisposable.Disposable = disposable; } else { onImageSet = () => _layerDisposable.Disposable = disposable; } if (willUpdateMeasures) { view.RequestLayout(); } else { view.Invalidate(); } _currentState = newState; } }
/// <summary> /// Updates or creates a sublayer to render a border-like shape. /// </summary> /// <param name="owner">The parent layer to apply the shape</param> /// <param name="area">The rendering area</param> /// <param name="background">The background brush</param> /// <param name="borderThickness">The border thickness</param> /// <param name="borderBrush">The border brush</param> /// <param name="cornerRadius">The corner radius</param> /// <param name="backgroundImage">The background image in case of a ImageBrush background</param> public void UpdateLayer( FrameworkElement owner, Brush background, Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius, object backgroundImage ) { // Bounds is captured to avoid calling twice calls below. var area = new Rect(0, 0, owner.ActualWidth, owner.ActualHeight); var newState = new LayoutState(area, background, borderThickness, borderBrush, cornerRadius, backgroundImage); var previousLayoutState = _currentState; if (!newState.Equals(previousLayoutState)) { if ( background != null || cornerRadius != CornerRadius.None || (borderThickness != Thickness.Empty && borderBrush != null) ) { _layerDisposable.Disposable = null; _layerDisposable.Disposable = InnerCreateLayer(owner, newState); } else { _layerDisposable.Disposable = null; } _currentState = newState; } }
//////////////////////////////////////////////////////////////////////////////////////////////// /*--------------------------------------------------------------------------------------------*/ internal void Build(IHoverboardPanelState pPanelState, LayoutState pLayoutState, IItemVisualSettingsProvider pItemVisualSettProv) { Vector2 dir = pLayoutState.ItemLayout.Direction; Vector2 pos = Vector2.zero; Vector2 posMin = Vector2.zero; Vector2 posMax = Vector2.zero; for (int i = 0; i < pLayoutState.FullItems.Length; i++) { BaseItemState itemState = pLayoutState.FullItems[i]; IItemVisualSettings visualSett = pItemVisualSettProv.GetSettings(itemState.Item); GameObject itemObj = (GameObject)itemState.Item.DisplayContainer; UiItem uiItem = itemObj.AddComponent <UiItem>(); uiItem.Build(pPanelState, pLayoutState, itemState, visualSett); uiItem.transform.localPosition = new Vector3(pos.x, 0, pos.y) * UiItem.Size; var itemSize = new Vector2(itemState.Item.Width, itemState.Item.Height); posMin = Vector2.Min(posMin, pos); posMax = Vector2.Max(posMax, pos + itemSize); pos += Vector2.Scale(itemSize, dir); } Vector2 size = posMax - posMin; Bounds = new Rect(posMin.x, posMin.y, size.x, size.y); gameObject.transform.localPosition = Vector3.zero; gameObject.transform.localRotation = Quaternion.identity; gameObject.transform.localScale = Vector3.one; }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.PopLink"/>. /// </summary> private void ProcessPopLinkToken(TextLayoutCommandStream output, ref TextParserToken token, ref LayoutState state, ref Int32 index) { output.WritePopLink(); state.AdvanceLineToNextCommand(); index++; }
protected override bool TryCalculateStateName(LayoutState layout, out string stateName) { // Only continue if there are rules if ((Rules != null) && (Rules.Count > 0)) { // Try to find the first matching rule var rule = Rules.Where((r) => r.Applies(layout)).FirstOrDefault(); // If found, make sure state name is valid and set it if (rule != null) { if (string.IsNullOrEmpty(rule.StateName)) { throw new InvalidOperationException("The matching rule did not provide a valid state name."); } // Return the state stateName = rule.StateName; // Success return true; } } // No matching rule found stateName = null; return false; }
public void TestLayout() { var dataSource = new TestDataSource(); new TestDataGen().GenerateDataItems(dataSource, 10, 0); var boxContainer = new BoxContainer(dataSource); TestDataGen.GenerateBoxSizes(boxContainer); var diagram = new Diagram(); diagram.Boxes = boxContainer; diagram.LayoutSettings.LayoutStrategies.Add("default", new LinearLayoutStrategy()); diagram.LayoutSettings.DefaultLayoutStrategyId = "default"; var state = new LayoutState(diagram) { BoxSizeFunc = dataId => boxContainer.BoxesByDataId[dataId].Size }; LayoutAlgorithm.Apply(state); Assert.AreEqual(5, diagram.VisualTree?.Depth); }
private void Cache(FlexibleView.Recycler recycler, LayoutState layoutState, bool immediate, float scrolled = 0) { if (layoutState.LayoutDirection == LayoutState.LAYOUT_END) { // get the first child in the direction we are going FlexibleView.ViewHolder child = GetChildClosestToEnd(); if (child != null) { if (child.ItemView.Focusable == false || mOrientationHelper.GetViewHolderEnd(child) + scrolled < mOrientationHelper.GetEnd()) { layoutState.Available = MAX_SCROLL_FACTOR * mOrientationHelper.GetTotalSpace(); layoutState.Extra = 0; layoutState.ScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN; layoutState.Recycle = false; Fill(recycler, layoutState, true, immediate); } } } else { FlexibleView.ViewHolder child = GetChildClosestToStart(); if (child != null) { if (child.ItemView.Focusable == false || mOrientationHelper.GetViewHolderStart(child) + scrolled > 0) { layoutState.Available = MAX_SCROLL_FACTOR * mOrientationHelper.GetTotalSpace(); layoutState.Extra = 0; layoutState.ScrollingOffset = LayoutState.SCROLLING_OFFSET_NaN; layoutState.Recycle = false; Fill(recycler, layoutState, true, immediate); } } } }
/// <summary> /// Updates or creates a sublayer to render a border-like shape. /// </summary> /// <param name="owner">The parent layer to apply the shape</param> /// <param name="area">The rendering area</param> /// <param name="background">The background brush</param> /// <param name="borderThickness">The border thickness</param> /// <param name="borderBrush">The border brush</param> /// <param name="cornerRadius">The corner radius</param> /// <param name="backgroundImage">The background image in case of a ImageBrush background</param> /// <returns>An updated BoundsPath if the layer has been created or updated; null if there is no change.</returns> public CGPath UpdateLayer( _View owner, Brush background, BackgroundSizing backgroundSizing, Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius, _Image backgroundImage) { // Bounds is captured to avoid calling twice calls below. var bounds = owner.Bounds; var area = new CGRect(0, 0, bounds.Width, bounds.Height); var newState = new LayoutState(area, background, backgroundSizing, borderThickness, borderBrush, cornerRadius, backgroundImage); var previousLayoutState = _currentState; if (!newState.Equals(previousLayoutState)) { #if __MACOS__ owner.WantsLayer = true; #endif _layerDisposable.Disposable = null; _layerDisposable.Disposable = InnerCreateLayer(owner as UIElement, owner.Layer, newState); _currentState = newState; } return(newState.BoundsPath); }
internal virtual void LayoutChunk(FlexibleViewRecycler recycler, LayoutState layoutState, LayoutChunkResult result) { FlexibleViewViewHolder holder = layoutState.Next(recycler); if (holder == null) { // if we are laying out views in scrap, this may return null which means there is // no more items to layout. result.Finished = true; return; } if (mShouldReverseLayout == (layoutState.LayoutDirection == LayoutState.LAYOUT_START)) { AddView(holder); } else { AddView(holder, 0); } result.Consumed = orientationHelper.GetViewHolderMeasurement(holder); float left, top, width, height; if (Orientation == VERTICAL) { width = Width - PaddingLeft - PaddingRight; height = result.Consumed; left = PaddingLeft; if (layoutState.LayoutDirection == LayoutState.LAYOUT_END) { top = layoutState.Offset; } else { top = layoutState.Offset - height; } LayoutChild(holder, left, top, width, height); } else { width = result.Consumed; height = Height - PaddingTop - PaddingBottom; top = PaddingTop; if (layoutState.LayoutDirection == LayoutState.LAYOUT_END) { left = layoutState.Offset; } else { left = layoutState.Offset - width; } LayoutChild(holder, left, top, width, height); } result.Focusable = true; }
public LinearLayoutManager(int orientation) { mOrientation = orientation; mOrientationHelper = OrientationHelper.CreateOrientationHelper(this, mOrientation); mLayoutState = new LayoutState(); mLayoutState.Offset = mOrientationHelper.GetStartAfterPadding(); }
public LayoutManagerState() { this.agvsToDispatch = new StringList(); this.agvsToRoute = new StringList(); this.blockedAgvs = new StringList(); this.congestedNodes = new StringList(); this.layout = new LayoutState(); }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.ToggleBold"/>. /// </summary> private void ProcessToggleBoldToken(TextLayoutCommandStream output, ref Boolean bold, ref LayoutState state, ref Int32 index) { output.WriteToggleBold(); state.AdvanceLineToNextCommand(); bold = !bold; index++; }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.ToggleItalic"/>. /// </summary> private void ProcessToggleItalicToken(TextLayoutCommandStream output, ref Boolean italic, ref LayoutState state, ref Int32 index) { output.WriteToggleItalic(); state.AdvanceLineToNextCommand(); italic = !italic; index++; }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.PopStyle"/>. /// </summary> private void ProcessPopStyleToken(TextLayoutCommandStream output, ref Boolean bold, ref Boolean italic, ref TextParserToken token, ref LayoutState state, ref Int32 index) { output.WritePopStyle(); state.AdvanceLineToNextCommand(); PopStyle(ref bold, ref italic); index++; }
public virtual void UpdateLayout() { if (_layoutState == LayoutState.Normal) { return; } if (_layoutState == LayoutState.Invalid) { // Full rearrange Point size; if (HorizontalAlignment != HorizontalAlignment.Stretch || VerticalAlignment != VerticalAlignment.Stretch) { size = Measure(_containerBounds.Size()); } else { size = _containerBounds.Size(); } if (size.X > _containerBounds.Width) { size.X = _containerBounds.Width; } if (size.Y > _containerBounds.Height) { size.Y = _containerBounds.Height; } // Align var controlBounds = LayoutUtils.Align(_containerBounds.Size(), size, HorizontalAlignment, VerticalAlignment); controlBounds.Offset(_containerBounds.Location); controlBounds.Offset(Left, Top); _bounds = controlBounds; _actualBounds = CalculateClientBounds(controlBounds); Arrange(); } else { // Only location MoveChildren(new Point(Left - _lastLocationHint.X, Top - _lastLocationHint.Y)); } _lastLocationHint = new Point(Left, Top); _layoutState = LayoutState.Normal; var ev = LayoutUpdated; if (ev != null) { ev(this, EventArgs.Empty); } }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.PushColor"/>. /// </summary> private void ProcessPushColorToken(TextLayoutCommandStream output, ref TextParserToken token, ref LayoutState state, ref Int32 index) { var pushedColor = ParseColor(token.Text); output.WritePushColor(new TextLayoutColorCommand(pushedColor)); state.AdvanceLineToNextCommand(); index++; }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.PushLink"/>. /// </summary> private void ProcessPushLinkToken(TextLayoutCommandStream output, ref TextParserToken token, ref LayoutState state, ref Int32 index) { var pushedLinkTargetIndex = RegisterLinkTargetWithCommandStream(output, token.Text); output.WritePushLink(new TextLayoutLinkCommand(pushedLinkTargetIndex)); state.AdvanceLineToNextCommand(); index++; }
/** * Tells the node that the current values in {@link #layout} have been seen. Subsequent calls * to {@link #hasNewLayout()} will return false until this node is laid out with new parameters. * You must call this each time the layout is generated if the node has a new layout. */ public void MarkLayoutSeen() { if (!HasNewLayout) { throw new InvalidOperationException("Expected node to have a new layout to be seen!"); } mLayoutState = LayoutState.UP_TO_DATE; }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.PushGlyphShader"/>. /// </summary> private void ProcessPushGlyphShaderToken(TextLayoutCommandStream output, ref TextParserToken token, ref LayoutState state, ref Int32 index) { var pushedGlyphShader = default(GlyphShader); var pushedGlyphShaderIndex = RegisterGlyphShaderWithCommandStream(output, token.Text, out pushedGlyphShader); output.WritePushGlyphShader(new TextLayoutGlyphShaderCommand(pushedGlyphShaderIndex)); state.AdvanceLineToNextCommand(); index++; }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.Custom"/>. /// </summary> private void ProcessCustomCommandToken(TextLayoutCommandStream output, ref TextParserToken token, ref LayoutState state, ref Int32 index) { var commandID = (token.TokenType - TextParserTokenType.Custom); var commandValue = token.Text.IsEmpty ? default(Int32) : StringSegmentConversion.ParseInt32(token.Text); output.WriteCustomCommand(new TextLayoutCustomCommand(commandID, commandValue)); state.AdvanceLineToNextCommand(); index++; }
private void QuickLayout() { var state = new LayoutState(m_diagram); state.BoxSizeFunc = dataId => m_diagram.Boxes.BoxesByDataId[dataId].Size; state.LayoutOptimizerFunc = LayoutOptimizer; LayoutAlgorithm.Apply(state); RenderBoxes(m_diagram.VisualTree, DrawCanvas); }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.PushFont"/>. /// </summary> private void ProcessPushFontToken(TextLayoutCommandStream output, ref TextParserToken token, ref LayoutState state, ref Int32 index) { var pushedFont = default(SpriteFont); var pushedFontIndex = RegisterFontWithCommandStream(output, token.Text, out pushedFont); output.WritePushFont(new TextLayoutFontCommand(pushedFontIndex)); state.AdvanceLineToNextCommand(); PushFont(pushedFont); index++; }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.PushStyle"/>. /// </summary> private void ProcessPushStyleToken(TextLayoutCommandStream output, ref Boolean bold, ref Boolean italic, ref TextParserToken token, ref LayoutState state, ref Int32 index) { var pushedStyle = default(TextStyle); var pushedStyleIndex = RegisterStyleWithCommandStream(output, token.Text, out pushedStyle); output.WritePushStyle(new TextLayoutStyleCommand(pushedStyleIndex)); state.AdvanceLineToNextCommand(); PushStyle(pushedStyle, ref bold, ref italic); index++; }
/// <summary> /// Updates or creates a sublayer to render a border-like shape. /// </summary> /// <param name="view">The view to which we should add the layers</param> /// <param name="background">The background brush of the border</param> /// <param name="borderThickness">The border thickness</param> /// <param name="borderBrush">The border brush</param> /// <param name="cornerRadius">The corner radius</param> /// <param name="padding">The padding to apply on the content</param> public void UpdateLayers( FrameworkElement view, Brush background, Thickness borderThickness, Brush borderBrush, CornerRadius cornerRadius, Thickness padding ) { // This is required because android Height and Width are hidden by Control. var baseView = view as View; Size targetSize = new Size(baseView.Width, baseView.Height); var drawArea = new Windows.Foundation.Rect(0, 0, targetSize.Width, targetSize.Height); var newState = new LayoutState(drawArea, background, borderThickness, borderBrush, cornerRadius, padding); var previousLayoutState = _currentState; if (!newState.Equals(previousLayoutState)) { bool imageHasChanged = newState.BackgroundImageSource != previousLayoutState?.BackgroundImageSource; bool shouldDisposeEagerly = imageHasChanged || newState.BackgroundImageSource == null; if (shouldDisposeEagerly) { // Clear previous value anyway in order to make sure the previous values are unset before the new ones. // This prevents the case where a second update would set a new background and then set the background to null when disposing the previous. _layerDisposable.Disposable = null; } if ( background != null || (borderThickness != Thickness.Empty && borderBrush != null) ) { Action onImageSet = null; var disposable = InnerCreateLayers(view, drawArea, background, borderThickness, borderBrush, cornerRadius, () => onImageSet?.Invoke()); // Most of the time we immediately dispose the previous layer. In the case where we're using an ImageBrush, // and the backing image hasn't changed, we dispose the previous layer at the moment the new background is applied, // to prevent a visible flicker. if (shouldDisposeEagerly) { _layerDisposable.Disposable = disposable; } else { onImageSet = () => _layerDisposable.Disposable = disposable; } } view.Invalidate(); _currentState = newState; } }
private static void OnCurrentStateChanged(BindableObject bindable, LayoutState oldValue, LayoutState newValue) { if (newValue == LayoutState.None) { Current.IsVisible = false; } else { Current.IsVisible = true; } }
public void CreateStateScaffold() { layoutState = new LayoutState(); List <StationState> stations = new List <StationState>(); foreach (var item in layout.Stations) { stations.Add(new StationState(item.Name)); } layoutState.StationStates = stations.ToArray(); layoutState.Seed = layout.InitialSeed; }
public override bool Applies(LayoutState layout) { if ((MinHeight > -1) && (layout.ActualHeight < MinHeight)) { return(false); } if ((MinWidth > -1) && (layout.ActualWidth < MinWidth)) { return(false); } return(true); }
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) { e.Handled = true; if (!((sender as Hyperlink)?.DataContext is SearchInstance instance)) { return; } CurrentSearchInstance = instance; current_layout = LayoutState.Three; ApplyTranslate(); }
public override bool Applies(LayoutState layout) { // If there are no child rules the answer is no if ((Rules == null) || (Rules.Count < 1)) { return false; } // At least one child must match foreach (var child in Rules) { // If child matches, success if (child.Applies(layout)) return true; } // No match return false; }
public override bool Applies(LayoutState layout) { // If there are no child rules the answer is no if ((Rules == null) || (Rules.Count < 1)) { return false; } // All children must match foreach (var child in Rules) { // Child doesn't match. Fail. if (!child.Applies(layout)) return false; } // Success return true; }
//************************************************************************* // Method: AsyncLayout_LayOutGraphCompleted() // /// <summary> /// Handles the LayOutGraphCompleted event on the m_oAsyncLayout object. /// </summary> /// /// <param name="oSender"> /// Standard event argument. /// </param> /// /// <param name="oAsyncCompletedEventArgs"> /// Standard event argument. /// </param> //************************************************************************* protected void AsyncLayout_LayOutGraphCompleted( Object oSender, AsyncCompletedEventArgs oAsyncCompletedEventArgs ) { AssertValid(); #if TRACE_LAYOUT_AND_DRAW Debug.WriteLine("NodeXLControl: AsyncLayout_LayOutGraphCompleted()"); #endif if (oAsyncCompletedEventArgs.Error == null) { // The asynchronous layout has completed and now the graph needs to // be drawn. m_eLayoutState = LayoutState.LayoutCompleted; LayOutOrDrawGraph(); } else { m_eLayoutState = LayoutState.Stable; } FireGraphLaidOut(oAsyncCompletedEventArgs); }
protected void Dirty() { if (layoutState == LayoutState.DIRTY) { return; } else if (layoutState == LayoutState.HAS_NEW_LAYOUT) { throw new InvalidOperationException ("Previous layout was ignored! MarkLayoutSeen() was never called."); } layoutState = LayoutState.DIRTY; if (parent != null) { parent.Dirty (); } }
public override bool Applies(LayoutState layout) { if ((MinHeight > -1) && (layout.ActualHeight < MinHeight)) { return false; } if ((MinWidth > -1) && (layout.ActualWidth < MinWidth)) { return false; } return true; }
//************************************************************************* // Method: LayOutOrDrawGraph() // /// <summary> /// Lays out or draws the graph, depending on the current layout state. /// </summary> //************************************************************************* protected void LayOutOrDrawGraph() { AssertValid(); #if TRACE_LAYOUT_AND_DRAW Debug.WriteLine("NodeXLControl: LayOutOrDrawGraph(), m_eLayoutState = " + m_eLayoutState); #endif Rect oGraphRectangle = this.GraphRectangle; #if TRACE_LAYOUT_AND_DRAW Debug.WriteLine("NodeXLControl: LayOutOrDrawGraph(), size = " + oGraphRectangle.Width + " , " + oGraphRectangle.Height); #endif System.Drawing.Rectangle oGraphRectangle2 = WpfGraphicsUtil.RectToRectangle(oGraphRectangle); switch (m_eLayoutState) { case LayoutState.Stable: break; case LayoutState.LayoutRequired: Debug.Assert(!m_oAsyncLayout.IsBusy); FireLayingOutGraph(); m_oLastLayoutContext = new LayoutContext(oGraphRectangle2); m_eLayoutState = LayoutState.LayingOut; if (m_oAsyncLayout is SortableLayoutBase) { // If the vertex layout order has been set, tell the layout // object to sort the vertices before laying them out. ( (SortableLayoutBase)m_oAsyncLayout ).VertexSorter = m_oGraph.ContainsKey( ReservedMetadataKeys.SortableLayoutOrderSet) ? new ByMetadataVertexSorter<Single>( ReservedMetadataKeys.SortableLayoutOrder) : null; } // Start an asynchronous layout. The m_oAsyncLayout object // will fire LayOutGraphIterationCompleted and // LayOutGraphCompleted events as it does its work. m_oAsyncLayout.LayOutGraphAsync( m_oGraph, m_oLastLayoutContext); break; case LayoutState.LayingOut: break; case LayoutState.LayoutIterationCompleted: // An iteration of the asynchronous layout has completed and // now the graph needs to be drawn. m_eLayoutState = LayoutState.LayingOut; DrawGraph(oGraphRectangle); break; case LayoutState.LayoutCompleted: // The asynchronous layout has completed and now the graph // needs to be drawn. m_eLayoutState = LayoutState.Stable; // Has the size of the control changed since the layout was // started? System.Drawing.Rectangle oLastGraphRectangle = m_oLastLayoutContext.GraphRectangle; if ( oLastGraphRectangle.Width != oGraphRectangle2.Width || oLastGraphRectangle.Height != oGraphRectangle2.Height ) { // Yes. Transform the layout to the new size. #if TRACE_LAYOUT_AND_DRAW Debug.WriteLine("NodeXLControl: Transforming layout."); #endif m_oLastLayoutContext = TransformLayout(oGraphRectangle); } DrawGraph(oGraphRectangle); break; case LayoutState.TransformRequired: // The control has been resized and now the graph's layout // needs to be transformed to the new size. m_oLastLayoutContext = TransformLayout(oGraphRectangle); m_eLayoutState = LayoutState.Stable; DrawGraph(oGraphRectangle); break; default: Debug.Assert(false); break; } }
/// <summary> /// Initializes a new <see cref="LayoutStateCalculatingEventArgs"/> instance. /// </summary> public LayoutStateCalculatingEventArgs(LayoutState layout) { // Validate if (layout == null) throw new ArgumentNullException("layout"); // Store this.Layout = layout; // Default values StateName = string.Empty; }
/// <summary> /// Attempts to calculate the state name for the associated object. /// </summary> /// <param name="layout"> /// A <see cref="LayoutState"/> instance that provides information about the layout /// of the application. /// </param> /// <param name="stateName"> /// If successful, the calculated state name; otherwise <see langword="null"/>. /// </param> /// <returns> /// <c>true</c> if the state name could be calculated; otherwise <c>false</c>. /// </returns> protected abstract bool TryCalculateStateName(LayoutState layout, out string stateName);
protected override bool TryCalculateStateName(LayoutState layout, out string stateName) { // Figure out difference between width and height var diff = Math.Abs(layout.ActualWidth - layout.ActualHeight); // If it's within the square threshold, call it square if (diff <= SquareThreshold) { stateName = SquareStateName; } // Wider than tall? else if (layout.ActualWidth > layout.ActualHeight) { stateName = LandscapeStateName; } else { stateName = PortraitStateName; } // Success return true; }
/// <summary> /// Given a string and an available space, returns the largest substring which will fit within that space. /// </summary> private Boolean GetFittedSubstring(SpriteFontFace font, Int32 maxLineWidth, ref StringSegment tokenText, ref Size2 tokenSize, ref LayoutState state, Boolean hyphenate) { var substringAvailableWidth = maxLineWidth - state.PositionX; var substringWidth = 0; var substringLength = 0; for (int glyphIndex = 0; glyphIndex < tokenText.Length - 1; glyphIndex++) { var glyph1 = tokenText[glyphIndex]; var glyph2 = tokenText[glyphIndex + 1]; var glyphWidth = 0; if (hyphenate) { glyphWidth = font.MeasureGlyph(glyph1, '-').Width + font.MeasureGlyph('-').Width; } else { glyphWidth = font.MeasureGlyph(glyph1).Width; } if (substringAvailableWidth - glyphWidth < 0) break; var glyphSize = font.MeasureGlyph(glyph1, glyph2); substringAvailableWidth -= glyphSize.Width; substringWidth += glyphSize.Width; substringLength++; } tokenText = substringLength > 0 ? tokenText.Substring(0, substringLength) : StringSegment.Empty; tokenSize = new Size2(substringWidth, tokenSize.Height); return substringLength > 0; }
/// <summary> /// Adds a <see cref="TextLayoutCommandType.Text"/> command to the output stream if the specified span of text has a non-zero length. /// </summary> private Boolean EmitTextIfNecessary(TextLayoutCommandStream output, Int32 start, Int32 length, ref Rectangle bounds, ref LayoutState state) { if (length == 0) return false; output.WriteText(new TextLayoutTextCommand(start, length, bounds.X, bounds.Y, (Int16)bounds.Width, (Int16)bounds.Height)); state.LineLengthInCommands++; return true; }
/// <summary> /// Accumulates sequential text tokens into a single text command. /// </summary> private Boolean AccumulateText(TextParserTokenStream input, TextLayoutCommandStream output, SpriteFontFace font, ref Int32 index, ref LayoutState state, ref TextLayoutSettings settings) { var hyphenate = (settings.Options & TextLayoutOptions.Hyphenate) == TextLayoutOptions.Hyphenate; var availableWidth = (settings.Width ?? Int32.MaxValue); var x = state.PositionX; var y = state.PositionY; var width = 0; var height = 0; var accumulatedStart = input[index].Text.Start + (state.ParserTokenOffset ?? 0); var accumulatedLength = 0; var accumulatedCount = 0; var lineOverflow = false; var lineBreakPossible = false; var tokenText = default(StringSegment); var tokenNext = default(TextParserToken?); var tokenSize = default(Size2); while (index < input.Count) { var token = input[index]; if (token.TokenType != TextParserTokenType.Text || token.IsNewLine) break; if (!IsSegmentForCurrentSource(token.Text)) { if (accumulatedCount > 0) break; EmitChangeSourceIfNecessary(input, output, ref token); } tokenText = token.Text.Substring(state.ParserTokenOffset ?? 0); tokenNext = GetNextTextToken(input, index); tokenSize = MeasureToken(font, token.TokenType, tokenText, tokenNext); // NOTE: We assume in a couple of places that tokens sizes don't exceed Int16.MaxValue, so try to // avoid accumulating tokens larger than that just in case somebody is doing something dumb if (width + tokenSize.Width > Int16.MaxValue) break; var overflowsLine = state.PositionX + tokenSize.Width > availableWidth; if (overflowsLine) { lineOverflow = true; break; } if (tokenText.Start != accumulatedStart + accumulatedLength) break; if (token.IsWhiteSpace && (state.LineBreakCommand == null || !token.IsNonBreakingSpace)) { lineBreakPossible = true; state.LineBreakCommand = output.Count; state.LineBreakOffset = accumulatedLength + token.Text.Length - 1; } width = width + tokenSize.Width; height = Math.Max(height, tokenSize.Height); accumulatedLength = accumulatedLength + tokenText.Length; accumulatedCount++; state.AdvanceLineToNextCommand(tokenSize.Width, tokenSize.Height, 1, tokenText.Length); state.ParserTokenOffset = 0; state.LineLengthInCommands--; index++; } if (lineBreakPossible) { var preLineBreakTextStart = accumulatedStart; var preLineBreakTextLength = state.LineBreakOffset.Value; var preLineBreakText = CreateStringSegmentFromCurrentSource(preLineBreakTextStart, preLineBreakTextLength); var preLineBreakSize = (preLineBreakText.Length == 0) ? Size2.Zero : MeasureToken(font, TextParserTokenType.Text, preLineBreakText); state.BrokenTextSizeBeforeBreak = preLineBreakSize; var postLineBreakStart = accumulatedStart + (state.LineBreakOffset.Value + 1); var postLineBreakLength = accumulatedLength - (state.LineBreakOffset.Value + 1); var postLineBreakText = CreateStringSegmentFromCurrentSource(postLineBreakStart, postLineBreakLength); var postLineBreakSize = (postLineBreakText.Length == 0) ? Size2.Zero : MeasureToken(font, TextParserTokenType.Text, postLineBreakText, GetNextTextToken(input, index - 1)); state.BrokenTextSizeAfterBreak = postLineBreakSize; } var bounds = new Rectangle(x, y, width, height); EmitTextIfNecessary(output, accumulatedStart, accumulatedLength, ref bounds, ref state); if (lineOverflow && !state.ReplaceLastBreakingSpaceWithLineBreak(output, ref settings)) { var overflowingToken = input[index]; if (overflowingToken.IsWhiteSpace && !overflowingToken.IsNonBreakingSpace) { output.WriteLineBreak(new TextLayoutLineBreakCommand(1)); state.AdvanceLineToNextCommand(0, 0, 1, 1, isLineBreak: true); state.AdvanceLayoutToNextLine(output, ref settings); if (overflowingToken.Text.Length > 1) { state.ParserTokenOffset = 1; } else { index++; } return true; } if (!GetFittedSubstring(font, availableWidth, ref tokenText, ref tokenSize, ref state, hyphenate) && state.LineWidth == 0) return false; var overflowingTokenBounds = (tokenText.Length == 0) ? Rectangle.Empty : new Rectangle(state.PositionX, state.PositionY, tokenSize.Width, tokenSize.Height); var overflowingTextEmitted = EmitTextIfNecessary(output, tokenText.Start, tokenText.Length, ref overflowingTokenBounds, ref state); if (overflowingTextEmitted) { state.AdvanceLineToNextCommand(tokenSize.Width, tokenSize.Height, 0, tokenText.Length); if (hyphenate) { output.WriteHyphen(); state.AdvanceLineToNextCommand(0, 0, 1, 0); } } state.ParserTokenOffset = (state.ParserTokenOffset ?? 0) + tokenText.Length; state.AdvanceLayoutToNextLine(output, ref settings); } return true; }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.PopGlyphShader"/>. /// </summary> private void ProcessPopGlyphShaderToken(TextLayoutCommandStream output, ref TextParserToken token, ref LayoutState state, ref Int32 index) { output.WritePopGlyphShader(); state.AdvanceLineToNextCommand(); index++; }
protected override bool TryCalculateStateName(out string stateName) { try { // TODO: Get args var view = ApplicationView.GetForCurrentView(); var bounds = (Window.Current != null ? Window.Current.Bounds : Rect.Empty); LayoutState layout = new LayoutState(); // See if we have event subscribers first if (LayoutStateCalculating != null) { // Wrap in event args var e = new LayoutStateCalculatingEventArgs(layout); // Raise the event LayoutStateCalculating(this, e); // If the calculation was canceled, bail if (e.IsCanceled) { stateName = null; return false; } // If the calculation was handled, process if (e.IsHandled) { stateName = e.StateName; return true; } } // No subscribers or none handled it. Pass to override. return TryCalculateStateName(layout, out stateName); } catch (Exception ex) { Debug.WriteLine("Could not calculate state name. {0}", ex.Message); stateName = null; return false; } }
/// <summary> /// Determines if the rule applies to the specified layout. /// </summary> /// <param name="layout"> /// The <see cref="LayoutState"/> instance to test. /// </param> /// <returns> /// <c>true</c> if the rule applies to the specified layout; otherwise <c>false</c>. /// </returns> public abstract bool Applies(LayoutState layout);
//************************************************************************* // Constructor: NodeXLControl() // /// <summary> /// Initializes a new instance of the <see cref="NodeXLControl" /> class. /// </summary> //************************************************************************* public NodeXLControl() { m_oGraph = new Graph(); m_oGraphDrawer = new GraphDrawer(this); m_oAsyncLayout = new FruchtermanReingoldLayout(); OnNewLayout(m_oAsyncLayout); m_oLastLayoutContext = new LayoutContext(System.Drawing.Rectangle.Empty); m_oLastGraphDrawingContext = null; m_eLayoutState = LayoutState.Stable; m_eMouseMode = MouseMode.Select; m_bMouseAlsoSelectsIncidentEdges = true; m_bAllowVertexDrag = true; m_oVerticesBeingDragged = null; m_oMarqueeBeingDragged = null; m_oTranslationBeingDragged = null; m_oSelectedVertices = new HashSet<IVertex>(); m_oSelectedEdges = new HashSet<IEdge>(); m_oCollapsedGroups = new Dictionary<String, IVertex>(); m_oDoubleClickedVertexInfo = null; m_bShowVertexToolTips = false; m_oLastMouseMoveLocation = new Point(-1, -1); // Create a helper object for displaying vertex tooltips. CreateVertexToolTipTracker(); m_oVertexToolTip = null; m_bGraphZoomCentered = false; this.AddLogicalChild(m_oGraphDrawer.VisualCollection); CreateTransforms(); // Prevent a focus rectangle from being drawn around the control when // it captures keyboard focus. The focus rectangle does not behave // properly when the layout and render transforms are applied -- // sometimes the rectangle disappears, and sometimes it gets magnified // by the render layout. this.FocusVisualStyle = null; // AssertValid(); }
//************************************************************************* // Method: OnRenderSizeChanged() // /// <summary> /// Raises the SizeChanged event, using the specified information as part /// of the eventual event data. /// </summary> /// /// <param name="sizeInfo"> /// Details of the old and new size involved in the change. /// </param> //************************************************************************* protected override void OnRenderSizeChanged( SizeChangedInfo sizeInfo ) { AssertValid(); // Note that this method gets called when the layout transform is // modified, not just when the control is resized. #if TRACE_LAYOUT_AND_DRAW Debug.WriteLine("NodeXLControl: OnRenderSizeChanged()," + " sizeInfo.NewSize = " + sizeInfo.NewSize); #endif base.OnRenderSizeChanged(sizeInfo); // The next block is an ugly workaround for the following problem. // // The center of the graph zoom should initially be placed at the // center of the control, so that if the zoom is first increased via // the GraphZoom property, the control will zoom from the center. (If // the zoom is first increased via the mouse wheel, the zoom center // will be placed at the mouse location.) This can't be done until the // final control size is known. // // A Loaded handler would be a logical place to do this, but the final // control size is not always available during the Loaded event, // depending on how the control is anchored or docked within its // parent. // // A workaround would be to set this control's RenderTransformOrigin to // (0.5, 0.5) when the transforms are first created, but the control's // transforms are designed to work with this property left at its // default value of the origin. // // TODO: Fix this! if (!m_bGraphZoomCentered && this.IsLoaded) { m_bGraphZoomCentered = true; CenterGraphZoom(); } if (m_eLayoutState != LayoutState.Stable) { // The new size will be detected by the LayoutState.LayoutCompleted // case in OnRender(). return; } // Make sure the size has actually changed. This event fires once at // startup and should be ignored then. System.Drawing.Rectangle oLastGraphRectangle = m_oLastLayoutContext.GraphRectangle; System.Drawing.Rectangle oNewGraphRectangle = WpfGraphicsUtil.RectToRectangle( new Rect(sizeInfo.NewSize) ); if ( oLastGraphRectangle.Width != oNewGraphRectangle.Width || oLastGraphRectangle.Height != oNewGraphRectangle.Height ) { m_eLayoutState = LayoutState.TransformRequired; LayOutOrDrawGraph(); // For the case where this method was called because the layout // transform was modified, make sure the graph wasn't moved too // far by the transform. LimitTranslation(); } }
//************************************************************************* // Method: AsyncLayout_LayOutGraphIterationCompleted() // /// <summary> /// Handles the LayOutGraphIterationCompleted event on the m_oAsyncLayout /// object. /// </summary> /// /// <param name="oSender"> /// Standard event argument. /// </param> /// /// <param name="oEventArgs"> /// Standard event argument. /// </param> //************************************************************************* protected void AsyncLayout_LayOutGraphIterationCompleted( Object oSender, EventArgs oEventArgs ) { AssertValid(); #if TRACE_LAYOUT_AND_DRAW Debug.WriteLine("NodeXLControl:" + " AsyncLayout_LayOutGraphIterationCompleted()"); #endif // Note that the worker thread on which the layout is occurring blocks // until this event handler returns. Therefore, it is okay for // LayOutOrDrawGraph() to use the graph's vertex locations and // metadata, even though the worker thread modifies that same data // while the worker thread is running. m_eLayoutState = LayoutState.LayoutIterationCompleted; LayOutOrDrawGraph(); }
//************************************************************************* // Method: DrawGraph() // /// <summary> /// Draws the graph after optionally laying it out. /// </summary> /// /// <param name="layOutGraphFirst"> /// If true, the graph is laid out again before it is drawn. If false, the /// graph is drawn using the current vertex locations. /// </param> /// /// <remarks> /// Graph layout occurs asynchronously after this method is called with a /// <paramref name="layOutGraphFirst" /> argument of true. See the <see /// cref="IsLayingOutGraph" /> property for details. /// /// <para> /// If the graph is currently being laid out, this method does nothing. /// </para> /// /// </remarks> //************************************************************************* public void DrawGraph( Boolean layOutGraphFirst ) { AssertValid(); if (this.IsLayingOutGraph) { return; } if (layOutGraphFirst) { m_eLayoutState = LayoutState.LayoutRequired; // Don't just call LayOutOrDrawGraph() here, as is done in the else // clause. Assuming that this method is being called at // application startup time, the control has not yet gone through // its measure cycle. If LayOutOrDrawGraph() were called instead // of InvalidateVisual(), the actual width and height of this // control would be zero when the layout begins, and the graph // wouldn't get laid out properly. this.InvalidateVisual(); } else { m_eLayoutState = LayoutState.LayoutCompleted; LayOutOrDrawGraph(); } }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.Icon"/>. /// </summary> private Boolean ProcessIconToken(TextLayoutCommandStream output, ref TextParserToken token, ref LayoutState state, ref TextLayoutSettings settings, ref Int32 index) { var icon = default(TextIconInfo); var iconIndex = RegisterIconWithCommandStream(output, token.Text, out icon); var iconSize = MeasureToken(null, token.TokenType, token.Text); if (state.PositionX + iconSize.Width > (settings.Width ?? Int32.MaxValue)) state.AdvanceLayoutToNextLine(output, ref settings); if (state.PositionY + iconSize.Height > (settings.Height ?? Int32.MaxValue)) return false; output.WriteIcon(new TextLayoutIconCommand(iconIndex, state.PositionX, state.PositionY, (Int16)iconSize.Width, (Int16)iconSize.Height)); state.AdvanceLineToNextCommand(iconSize.Width, iconSize.Height, 1, 1); index++; return true; }
/// <summary> /// Processes a parser token with type <see cref="TextParserTokenType.Text"/>. /// </summary> private Boolean ProcessTextToken(TextParserTokenStream input, TextLayoutCommandStream output, SpriteFontFace currentFontFace, ref TextParserToken token, ref LayoutState state, ref TextLayoutSettings settings, ref Int32 index) { if (token.IsNewLine) { state.AdvanceLayoutToNextLineWithBreak(output, token.SourceLength, ref settings); index++; } else { if (!AccumulateText(input, output, currentFontFace, ref index, ref state, ref settings)) return false; } return true; }
/// <summary> /// Calculates a layout for the specified text. /// </summary> /// <param name="input">The parsed text which will be laid out according to the specified settings.</param> /// <param name="output">The layout command stream which will be populated with commands by this operation.</param> /// <param name="settings">A <see cref="TextLayoutSettings"/> structure which contains the settings for this operation.</param> public void CalculateLayout(TextParserTokenStream input, TextLayoutCommandStream output, TextLayoutSettings settings) { Contract.Require(input, "input"); Contract.Require(output, "output"); if (settings.Font == null) throw new ArgumentException(UltravioletStrings.InvalidLayoutSettings); var state = new LayoutState() { LineInfoCommandIndex = 1 }; output.Clear(); output.SourceText = input.SourceText; output.ParserOptions = input.ParserOptions; var acquiredPointers = !output.HasAcquiredPointers; if (acquiredPointers) output.AcquirePointers(); output.WriteBlockInfo(); output.WriteLineInfo(); var bold = (settings.Style == SpriteFontStyle.Bold || settings.Style == SpriteFontStyle.BoldItalic); var italic = (settings.Style == SpriteFontStyle.Italic || settings.Style == SpriteFontStyle.BoldItalic); if (settings.InitialLayoutStyle != null) PrepareInitialStyle(output, ref bold, ref italic, ref settings); var currentFont = settings.Font; var currentFontFace = settings.Font.GetFace(SpriteFontStyle.Regular); var index = 0; var processing = true; while (index < input.Count && processing) { if (state.PositionY >= (settings.Height ?? Int32.MaxValue)) break; var token = input[index]; currentFontFace = default(SpriteFontFace); currentFont = GetCurrentFont(ref settings, bold, italic, out currentFontFace); switch (token.TokenType) { case TextParserTokenType.Text: processing = ProcessTextToken(input, output, currentFontFace, ref token, ref state, ref settings, ref index); break; case TextParserTokenType.Icon: processing = ProcessIconToken(output, ref token, ref state, ref settings, ref index); break; case TextParserTokenType.ToggleBold: ProcessToggleBoldToken(output, ref bold, ref state, ref index); break; case TextParserTokenType.ToggleItalic: ProcessToggleItalicToken(output, ref italic, ref state, ref index); break; case TextParserTokenType.PushFont: ProcessPushFontToken(output, ref token, ref state, ref index); break; case TextParserTokenType.PushColor: ProcessPushColorToken(output, ref token, ref state, ref index); break; case TextParserTokenType.PushStyle: ProcessPushStyleToken(output, ref bold, ref italic, ref token, ref state, ref index); break; case TextParserTokenType.PushGlyphShader: ProcessPushGlyphShaderToken(output, ref token, ref state, ref index); break; case TextParserTokenType.PopFont: ProcessPopFontToken(output, ref token, ref state, ref index); break; case TextParserTokenType.PopColor: ProcessPopColorToken(output, ref token, ref state, ref index); break; case TextParserTokenType.PopStyle: ProcessPopStyleToken(output, ref bold, ref italic, ref token, ref state, ref index); break; case TextParserTokenType.PopGlyphShader: ProcessPopGlyphShaderToken(output, ref token, ref state, ref index); break; default: throw new InvalidOperationException(UltravioletStrings.UnrecognizedLayoutCommand.Format(token.TokenType)); } } state.FinalizeLayout(output, ref settings); if (acquiredPointers) output.ReleasePointers(); ClearLayoutStacks(); }