public string Export( ) { string xmlExport = "<Note " + sDefaultNoteAttribs + ">"; // first, sort all controls by Y. That way, if something was created and then moved UP, it won't // have a negative value ChildControls.Sort(delegate(IUIControl a, IUIControl b) { if (a.GetFrame( ).Top < b.GetFrame( ).Top) { return(-1); } return(1); }); foreach (IUIControl child in ChildControls) { IEditableUIControl editableChild = child as IEditableUIControl; xmlExport += editableChild.Export(new RectangleF(Padding.Left, Padding.Top, 0, 0), 0); } xmlExport += "</Note>"; return(xmlExport); }
protected void LayoutStackPanel(RectangleF bounds, float leftPadding, float topPadding, float availableWidth, float bottomPadding, float borderPaddingPx) { // layout all controls float yOffset = bounds.Y + topPadding + borderPaddingPx; //vertically they should just stack // now we must center each control within the stack. foreach (IUIControl control in ChildControls) { RectangleF controlFrame = control.GetFrame( ); RectangleF controlMargin = control.GetMargin( ); // horizontally position the controls according to their // requested alignment Alignment controlAlignment = control.GetHorzAlignment( ); // adjust by our position float xAdjust = 0; switch (controlAlignment) { case Alignment.Center: { xAdjust = bounds.X + ((availableWidth / 2) - (controlFrame.Width / 2)); break; } case Alignment.Right: { xAdjust = bounds.X + (availableWidth - (controlFrame.Width + controlMargin.Width)); break; } case Alignment.Left: { xAdjust = bounds.X; break; } } // adjust the next sibling by yOffset control.AddOffset(xAdjust + leftPadding + borderPaddingPx, yOffset); // and the next sibling must begin there yOffset = control.GetFrame( ).Bottom + controlMargin.Height; } // we need to store our bounds. We cannot // calculate them on the fly because we // would lose any control defined offsets, which would throw everything off. bounds.Height = (yOffset - bounds.Y) + bottomPadding + borderPaddingPx; // and store that as our bounds BorderView.Frame = bounds; Frame = bounds; // store our debug frame SetDebugFrame(Frame); // sort everything ChildControls.Sort(BaseControl.Sort); }
public Paragraph(CreateParams parentParams, XmlReader reader) { Initialize( ); // Always get our style first mStyle = parentParams.Style; Styles.Style.ParseStyleAttributesWithDefaults(reader, ref mStyle, ref ControlStyles.mParagraph); // check for attributes we support RectangleF bounds = new RectangleF( ); SizeF parentSize = new SizeF(parentParams.Width, parentParams.Height); ParseCommonAttribs(reader, ref parentSize, ref bounds); // Get margins and padding RectangleF padding; RectangleF margin; GetMarginsAndPadding(ref mStyle, ref parentSize, ref bounds, out margin, out padding); // apply margins to as much of the bounds as we can (bottom must be done by our parent container) ApplyImmediateMargins(ref bounds, ref margin, ref parentSize); Margin = margin; // check for border styling int borderPaddingPx = 0; if (mStyle.mBorderColor.HasValue) { BorderView.BorderColor = mStyle.mBorderColor.Value; } if (mStyle.mBorderRadius.HasValue) { BorderView.CornerRadius = mStyle.mBorderRadius.Value; } if (mStyle.mBorderWidth.HasValue) { BorderView.BorderWidth = mStyle.mBorderWidth.Value; borderPaddingPx = (int)Rock.Mobile.Graphics.Util.UnitToPx(mStyle.mBorderWidth.Value + PrivateNoteConfig.BorderPadding); } if (mStyle.mBackgroundColor.HasValue) { BorderView.BackgroundColor = mStyle.mBackgroundColor.Value; } // // now calculate the available width based on padding. (Don't actually change our width) float availableWidth = bounds.Width - padding.Left - padding.Width - (borderPaddingPx * 2); // see if there's a URL we should care about ActiveUrl = reader.GetAttribute("Url"); string urlLaunchesExternalBrowser = reader.GetAttribute("UrlLaunchesExternalBrowser"); if (string.IsNullOrEmpty(urlLaunchesExternalBrowser) == false) { UrlLaunchesExternalBrowser = bool.Parse(urlLaunchesExternalBrowser); } string urlUsesRockImpersonation = reader.GetAttribute("UrlUsesRockImpersonation"); if (string.IsNullOrEmpty(urlUsesRockImpersonation) == false) { UrlUsesRockImpersonation = bool.Parse(urlUsesRockImpersonation); } // now read what our children's alignment should be // check for alignment string result = reader.GetAttribute("ChildAlignment"); if (string.IsNullOrEmpty(result) == false) { switch (result) { case "Left": { ChildHorzAlignment = Alignment.Left; break; } case "Right": { ChildHorzAlignment = Alignment.Right; break; } case "Center": { ChildHorzAlignment = Alignment.Center; break; } default: { ChildHorzAlignment = mStyle.mAlignment.Value; break; } } } else { // if it wasn't specified, use LEFT alignment. ChildHorzAlignment = Alignment.Left; } bool removedLeadingWhitespace = false; bool lastControlWasElement = false; bool finishedReading = false; while (finishedReading == false && reader.Read( )) { switch (reader.NodeType) { case XmlNodeType.Element: { IUIControl control = Parser.TryParseControl(new CreateParams(this, availableWidth, parentParams.Height, ref mStyle), reader); if (control != null) { // if the last control was an element (NoteText or Reveal), then we have two in a row. So place a space between them! if (lastControlWasElement) { NoteText textLabel = Parser.CreateNoteText(new CreateParams(this, availableWidth, parentParams.Height, ref mStyle), " "); ChildControls.Add(textLabel); } // only allow RevealBoxes / NoteText as children. if (control as RevealBox == null && control as NoteText == null) { throw new Exception(String.Format("Paragraph only supports children of type <RevealBox> or <NoteText>. Found <{0}>", control.GetType( ))); } ChildControls.Add(control); // flag that whitespace is removed, because either // this was the first control and we didn't want to, or // it was removed by the first text we created. removedLeadingWhitespace = true; // flag that the last control placed was a reveal, so that // should we come across another one immediately, we know to insert a space // so they don't render concatenated. lastControlWasElement = true; } break; } case XmlNodeType.Text: { // give the text a style that doesn't include things it shouldn't inherit Styles.Style textStyle = mStyle; textStyle.mBorderColor = null; textStyle.mBorderRadius = null; textStyle.mBorderWidth = null; // grab the text. remove any weird characters string text = Regex.Replace(reader.Value, @"\t|\n|\r", ""); if (removedLeadingWhitespace == false) { removedLeadingWhitespace = true; text = text.TrimStart(' '); } // now break it into words so we can do word wrapping string[] words = text.Split(' '); foreach (string word in words) { // create labels out of each one if (string.IsNullOrEmpty(word) == false) { // if the last thing we added was a special control like a reveal box, we // need the first label after that to have a leading space so it doesn't bunch up against // the control string nextWord = word; if (lastControlWasElement) { nextWord = word.Insert(0, " "); lastControlWasElement = false; } NoteText wordLabel = Parser.CreateNoteText(new CreateParams(this, availableWidth, parentParams.Height, ref textStyle), nextWord + " "); ChildControls.Add(wordLabel); } } lastControlWasElement = false; break; } case XmlNodeType.EndElement: { // if we hit the end of our label, we're done. //if( reader.Name == "Paragraph" || reader.Name == "P" ) if (ElementTagMatches(reader.Name)) { finishedReading = true; } break; } } } // should we add a URL Glyph? We're gonna be clever and add it AS a NoteText, so that it integrates with the paragraph nicely. // now add our glyph, if relevant TryAddUrlGlyph(availableWidth, parentParams.Height); // layout all controls // paragraphs are tricky. // We need to lay out controls horizontally and wrap when we run out of room. // To align, we need to keep track of each "row". When the row is full, // we calculate its width, and then adjust each item IN that row so // that the row is centered within the max width of the paragraph. // The max width of the paragraph is defined as the widest row. // maintain a list of all our rows so that once they are all generated, // we can align them based on the widest row. float maxRowWidth = 0; List <List <IUIControl> > rowList = new List <List <IUIControl> >( ); // track where within a row we need to start a control float rowRemainingWidth = availableWidth; float startingX = bounds.X + padding.Left + borderPaddingPx; // always store the last placed control's height so that should // our NEXT control need to wrap, we know how far down to wrap. float yOffset = bounds.Y + padding.Top + borderPaddingPx; float lastControlHeight = 0; float rowWidth = 0; //Create our first row and put it in our list List <IUIControl> currentRow = new List <IUIControl>( ); rowList.Add(currentRow); foreach (IUIControl control in ChildControls) { RectangleF controlFrame = control.GetFrame( ); // if there is NOT enough room on this row for the next control if (rowRemainingWidth < controlFrame.Width) { // since we're advancing to the next row, trim leading white space, which, if we weren't wrapping, // would be a space between words. // note: we can safely cast to a NoteText because that's the only child type we allow. string text = ((NoteText)control).GetText( ).TrimStart(' '); ((NoteText)control).SetText(text); // advance to the next row yOffset += lastControlHeight; // Reset values for the new row rowRemainingWidth = availableWidth; startingX = bounds.X + padding.Left + borderPaddingPx; lastControlHeight = 0; rowWidth = 0; currentRow = new List <IUIControl>( ); rowList.Add(currentRow); } // Add this next control to the current row currentRow.Add(control); // position this control appropriately control.AddOffset(startingX, yOffset); // update so the next child begins beyond this one. // also reduce the available width by this control's. rowWidth += controlFrame.Width; startingX += controlFrame.Width; //Increment startingX so the next control is placed after this one. rowRemainingWidth -= controlFrame.Width; //Reduce the available width by what this control took. lastControlHeight = controlFrame.Height > lastControlHeight ? controlFrame.Height : lastControlHeight; //Store the height of the tallest control on this row. // track the widest row maxRowWidth = rowWidth > maxRowWidth ? rowWidth : maxRowWidth; } // give each row the legal bounds it may work with RectangleF availableBounds = new RectangleF(bounds.X + padding.Left + borderPaddingPx, bounds.Y + borderPaddingPx + padding.Top, availableWidth, bounds.Height); // Now that we know the widest row, align all the rows foreach (List <IUIControl> row in rowList) { AlignRow(availableBounds, row, maxRowWidth); } // Build our final frame that determines our dimensions RectangleF frame = new RectangleF(65000, 65000, -65000, -65000); // for each child control foreach (IUIControl control in ChildControls) { // enlarge our frame by the current frame and the next child frame = Parser.CalcBoundingFrame(frame, control.GetFrame( )); } frame.Y = bounds.Y; frame.X = bounds.X; frame.Height += padding.Height + padding.Top + (borderPaddingPx * 2); //add in padding frame.Width = bounds.Width; // setup our bounding rect for the border frame = new RectangleF(frame.X, frame.Y, frame.Width, frame.Height); // and store that as our bounds BorderView.Frame = frame; Frame = frame; SetDebugFrame(Frame); // sort everything ChildControls.Sort(BaseControl.Sort); }
public Canvas(CreateParams parentParams, XmlReader reader) { Initialize( ); // Always get our style first mStyle = parentParams.Style; Styles.Style.ParseStyleAttributesWithDefaults(reader, ref mStyle, ref ControlStyles.mCanvas); // check for attributes we support RectangleF bounds = new RectangleF( ); SizeF parentSize = new SizeF(parentParams.Width, parentParams.Height); ParseCommonAttribs(reader, ref parentSize, ref bounds); // Get margins and padding RectangleF padding; RectangleF margin; GetMarginsAndPadding(ref mStyle, ref parentSize, ref bounds, out margin, out padding); // apply margins to as much of the bounds as we can (bottom must be done by our parent container) ApplyImmediateMargins(ref bounds, ref margin, ref parentSize); Margin = margin; // check for border styling int borderPaddingPx = 0; if (mStyle.mBorderColor.HasValue) { BorderView.BorderColor = mStyle.mBorderColor.Value; } if (mStyle.mBorderRadius.HasValue) { BorderView.CornerRadius = mStyle.mBorderRadius.Value; } if (mStyle.mBorderWidth.HasValue) { BorderView.BorderWidth = mStyle.mBorderWidth.Value; borderPaddingPx = (int)Rock.Mobile.Graphics.Util.UnitToPx(mStyle.mBorderWidth.Value + PrivateNoteConfig.BorderPadding); } if (mStyle.mBackgroundColor.HasValue) { BorderView.BackgroundColor = mStyle.mBackgroundColor.Value; } // // now calculate the available width based on padding. (Don't actually change our width) float availableWidth = bounds.Width - padding.Left - padding.Width - (borderPaddingPx * 2); // now read what our children's alignment should be // check for alignment string result = reader.GetAttribute("ChildAlignment"); if (string.IsNullOrEmpty(result) == false) { switch (result) { case "Left": ChildHorzAlignment = Alignment.Left; break; case "Right": ChildHorzAlignment = Alignment.Right; break; case "Center": ChildHorzAlignment = Alignment.Center; break; default: ChildHorzAlignment = mStyle.mAlignment.Value; break; } } else { // if it wasn't specified, use OUR alignment. ChildHorzAlignment = mStyle.mAlignment.Value; } // Parse Child Controls bool finishedParsing = false; while (finishedParsing == false && reader.Read( )) { switch (reader.NodeType) { case XmlNodeType.Element: { // let each child have our available width. Style style = new Style( ); style = mStyle; style.mAlignment = ChildHorzAlignment; IUIControl control = Parser.TryParseControl(new CreateParams(this, availableWidth, parentParams.Height, ref style), reader); if (control != null) { ChildControls.Add(control); } break; } case XmlNodeType.EndElement: { // if we hit the end of our label, we're done. //if( reader.Name == "Canvas" || reader.Name == "C" ) if (ElementTagMatches(reader.Name)) { finishedParsing = true; } break; } } } // layout all controls float yOffset = bounds.Y + padding.Top + borderPaddingPx; //vertically they should just stack float height = 0; // now we must center each control within the stack. foreach (IUIControl control in ChildControls) { RectangleF controlFrame = control.GetFrame( ); RectangleF controlMargin = control.GetMargin( ); // horizontally position the controls according to their // requested alignment Alignment controlAlignment = control.GetHorzAlignment( ); // adjust by our position float xAdjust = 0; switch (controlAlignment) { case Alignment.Center: xAdjust = bounds.X + ((availableWidth / 2) - (controlFrame.Width / 2)); break; case Alignment.Right: xAdjust = bounds.X + (availableWidth - (controlFrame.Width + controlMargin.Width)); break; case Alignment.Left: xAdjust = bounds.X; break; } // adjust the next sibling by yOffset control.AddOffset(xAdjust + padding.Left + borderPaddingPx, yOffset); // track the height of the grid by the control lowest control height = (control.GetFrame( ).Bottom + +controlMargin.Height) > height ? (control.GetFrame( ).Bottom + +controlMargin.Height) : height; } // we need to store our bounds. We cannot // calculate them on the fly because we // would lose any control defined offsets, which would throw everything off. bounds.Height = height + padding.Height + borderPaddingPx; // setup our bounding rect for the border bounds = new RectangleF(bounds.X, bounds.Y, bounds.Width, bounds.Height); // and store that as our bounds BorderView.Frame = bounds; Frame = bounds; // store our debug frame SetDebugFrame(Frame); // sort everything ChildControls.Sort(BaseControl.Sort); }
public List(CreateParams parentParams, XmlReader reader) { Initialize( ); // Always get our style first mStyle = parentParams.Style; Styles.Style.ParseStyleAttributesWithDefaults(reader, ref mStyle, ref ControlStyles.mList); // check for attributes we support RectangleF bounds = new RectangleF( ); SizeF parentSize = new SizeF(parentParams.Width, parentParams.Height); ParseCommonAttribs(reader, ref parentSize, ref bounds); // Get margins and padding RectangleF padding; RectangleF margin; GetMarginsAndPadding(ref mStyle, ref parentSize, ref bounds, out margin, out padding); // apply margins to as much of the bounds as we can (bottom must be done by our parent container) ApplyImmediateMargins(ref bounds, ref margin, ref parentSize); Margin = margin; // check for border styling int borderPaddingPx = 0; if (mStyle.mBorderColor.HasValue) { BorderView.BorderColor = mStyle.mBorderColor.Value; } if (mStyle.mBorderRadius.HasValue) { BorderView.CornerRadius = mStyle.mBorderRadius.Value; } if (mStyle.mBorderWidth.HasValue) { BorderView.BorderWidth = mStyle.mBorderWidth.Value; borderPaddingPx = (int)Rock.Mobile.Graphics.Util.UnitToPx(mStyle.mBorderWidth.Value + PrivateNoteConfig.BorderPadding); } if (mStyle.mBackgroundColor.HasValue) { BorderView.BackgroundColor = mStyle.mBackgroundColor.Value; } // // convert indentation if it's a percentage float listIndentation = mStyle.mListIndention.Value; if (listIndentation < 1) { listIndentation = parentParams.Width * listIndentation; } // now calculate the available width based on padding. (Don't actually change our width) // also consider the indention amount of the list. float availableWidth = bounds.Width - padding.Left - padding.Width - listIndentation - (borderPaddingPx * 2); // parse for the desired list style. Default to Bullet if they didn't put anything. ListType = reader.GetAttribute("Type"); if (string.IsNullOrEmpty(ListType) == true) { ListType = ListTypeBullet; } // Parse Child Controls int numberedCount = 1; // don't force our alignment, borders, bullet style or indentation on children. Style style = new Style( ); style = mStyle; style.mAlignment = null; style.mListIndention = null; style.mListBullet = null; style.mBorderColor = null; style.mBorderRadius = null; style.mBorderWidth = null; bool finishedParsing = false; while (finishedParsing == false && reader.Read( )) { switch (reader.NodeType) { case XmlNodeType.Element: { // Create the prefix for this list item. string listItemPrefixStr = mStyle.mListBullet + " "; if (ListType == ListTypeNumbered) { listItemPrefixStr = numberedCount.ToString() + ". "; } NoteText textLabel = Parser.CreateNoteText(new CreateParams(this, availableWidth, parentParams.Height, ref style), listItemPrefixStr); ChildControls.Add(textLabel); // create our actual child, but throw an exception if it's anything but a ListItem. IUIControl control = Parser.TryParseControl(new CreateParams(this, availableWidth - textLabel.GetFrame().Width, parentParams.Height, ref style), reader); ListItem listItem = control as ListItem; if (listItem == null) { throw new Exception(String.Format("Only a <ListItem> may be a child of a <List>. Found element <{0}>.", control.GetType( ))); } // if it will actually use the bullet point, increment our count. if (listItem.ShouldShowBulletPoint() == true) { numberedCount++; } else { // otherwise give it a blank space, and keep our count the same. textLabel.SetText(" "); } // and finally add the actual list item. ChildControls.Add(control); break; } case XmlNodeType.EndElement: { // if we hit the end of our label, we're done. //if( reader.Name == "List" || reader.Name == "L" ) if (ElementTagMatches(reader.Name)) { finishedParsing = true; } break; } } } // layout all controls float xAdjust = bounds.X + listIndentation; float yOffset = bounds.Y + padding.Top + borderPaddingPx; //vertically they should just stack // we know each child is a NoteText followed by ListItem. So, lay them out // as: * - ListItem // * - ListItem foreach (IUIControl control in ChildControls) { // position the control control.AddOffset(xAdjust + padding.Left + borderPaddingPx, yOffset); RectangleF controlFrame = control.GetFrame( ); RectangleF controlMargin = control.GetMargin( ); // is this the item prefix? if ((control as NoteText) != null) { // and update xAdjust so the actual item starts after. xAdjust += controlFrame.Width; } else { // reset the values for the next line. xAdjust = bounds.X + listIndentation; yOffset = controlFrame.Bottom + controlMargin.Height; } } // we need to store our bounds. We cannot // calculate them on the fly because we // would lose any control defined offsets, which would throw everything off. bounds.Height = (yOffset - bounds.Y) + padding.Height + borderPaddingPx; Frame = bounds; BorderView.Frame = bounds; // store our debug frame SetDebugFrame(Frame); // sort everything ChildControls.Sort(BaseControl.Sort); }