Consider the template:
Company
Logo (Image field)
Company Name (Text field)
The Company template has standard values set on both fields. If we get a Company item and save it using Glass without making any changes, the Logo field no longer uses the standard value. (The Company Name field is untouched.)
The issue, it seems, is that the Glass.Mapper.Sc.DataMappers.SitecoreFieldImageMapper serializes the value of that field differently than Sitecore does. When it tries to save, it thinks it's a change to the field and no longer uses the standard value.
Standard Value:
<image mediaid="{GUID}" />
Glass-generated Value:
<image height="64" width="64" mediaid="{GUID}" alt="Alt text" />
Is there a way to make Glass generate the same output as Sitecore?
I think that problem is in a way how SitecoreFieldImageMapper map ImageField to Image. For getting Height, Width and Alt are used public properties. If we look on them via reflector we will see that values of it get not directly from field:
public string Alt
{
get
{
string text = base.GetAttribute("alt");
if (text.Length == 0)
{
Item item = this.MediaItem;
if (item != null)
{
MediaItem mediaItem = item;
text = mediaItem.Alt;
if (text.Length == 0)
{
text = item["Alt"];
}
}
}
return text;
}
set
{
base.SetAttribute("alt", value);
}
}
If field does not contain value(e.g. for "alt": if (text.Length == 0)) then value will be received from MediaItem that is linked. It cause adding Height, Width and Alt from media library item after saving of field.
To fix this problem you could try replace this code:
int height = 0;
int.TryParse(field.Height, out height);
int width = 0;
int.TryParse(field.Width, out width);
img.Alt = field.Alt;
img.Height = height;
img.Width = width;
with direct getting attributes rather than usage of properties:
int height = 0;
if(int.TryParse(field.GetAttribute("height"), out height))
{
img.Height = height;
}
int width = 0;
if(int.TryParse(field.GetAttribute("width"), out width))
{
img.Width = width;
}
img.Alt = field.GetAttribute("alt");
With Alt property everything should be ok. But there could be problems with Width and Height as they are not Nullable and I am not sure how GlassMapper will handle Image with Width and Height that you haven't set.
Related
I am using this code to set the initial size of my buttons:
// Now check button sizes
auto *pDC = m_btnPreview.GetDC();
if (pDC != nullptr)
{
pDC->SelectObject(GetFont());
auto sizeActualSize = pDC->GetTextExtent(strButtonText);
sizeActualSize.cx += 10;
if (sizeActualSize.cx > rctOK.Width())
{
// We need to consistently resize both buttons
int iButtonGap = rctCancel.left - rctOK.right;
rctCancel.left = rctCancel.right - sizeActualSize.cx;
rctOK.right = rctCancel.left - iButtonGap;
rctOK.left = rctOK.right - sizeActualSize.cx;
GetDlgItem(IDOK)->MoveWindow(rctCancel);
m_btnPreview.MoveWindow(rctOK);
}
ReleaseDC(pDC);
}
It works fine. I correctly set the font so that the size returned is right. But I have added a value of 10 to pad it a bit so that the button looks nicer:
Is there a specific MFC constant for the default gap around button? Rather than fudging it with a value of 10?
I'm trying to pre-render some Text elements offscreen to retrieve their widths before rendering them later on-screen:
Group offScreenRoot = new Group();
Scene offScreen = new Scene(offScreenRoot, 1024, 768);
Set<Text> textSet = new HashSet<>();
Text textText = new Text(getText());
textSet.add(textText);
Text idText = new Text(getId());
textSet.add(idText);
for (String s : getStrings()) {
textSet.add(new Text(s));
}
for (Text text : textSet) {
text.setStyle("-fx-font-size: 48;"); // <- HERE!
text.applyCss();
offScreenRoot.getChildren().add(text);
}
for (Text text : textSet) {
System.out.println(text.getLayoutBounds().getWidth());
}
I'm applying a large font size via CSS, but this doesn't get picked up. Instead, the Text is as wide as it would be, rendered in the default font size (I guess).
Is there a way to render a Text off-screen and get the actual width value as per CSS font size?
I am not sure to understand the purpose of your code. It would have been easier with a MCVE, please keep it mind for the next time.
I think you made some simple mistakes. Looking at the applyCss definition You should apply the applyCss on the parent's node, not on the node.
Another thing important in the doc is :
Provided that the Node's Scene is not null
And unfortunately when you apply your CSS, the text's scene is null as it not yet added to his parent.
text.applyCss();
offScreenRoot.getChildren().add(text);
So there is two solutions, as described in the code below in comment (and especially in the for loop where you add Texts to the parent) :
public class MainOffscreen extends Application {
private List<String> stringsList = new ArrayList<>();
#Override
public void start(Stage primaryStage) throws Exception {
// Populates stringsList
stringsList.add("String1");
stringsList.add("String2");
stringsList.add("String3");
stringsList.add("String4");
VBox offscreenRootVbox = new VBox(10.0);
Scene offScreen = new Scene(offscreenRootVbox, 900, 700);
// Replace the Hashset by list in order to keep order, more interesting for testing.
List<Text> textSet = new ArrayList();
// Text text.
Text textText = new Text("Text");
textSet.add(textText);
// Text id.
Text idText = new Text("Id");
textSet.add(idText);
// Populate String list.
for(String s: stringsList) {
textSet.add(new Text(s));
}
// Print the width of Texts before applying Css.
for(Text text: textSet) {
System.out.println("BEFORE Width of " + text.getText() + " : " + text.getLayoutBounds().getWidth());
}
System.out.println("\n----------\n");
for(Text text: textSet) {
text.setStyle("-fx-font-size: 48;"); // <- HERE!
// First add it to the parent.
offscreenRootVbox.getChildren().add(text);
// Either apply the CSS on the each Text, or Apply it on the parent's Text, I choose the
// second solution.
// text.applyCss();
}
// Apply the css on the parent's node
offscreenRootVbox.applyCss();
for(Text text: textSet) {
System.out.println("AFTER Width of " + text.getText() + " : " + text.getLayoutBounds().getWidth());
}
// primaryStage.setScene(offScreen);
// primaryStage.show();
}
}
And it gives this output :
BEFORE Width of Text : 22.13671875
BEFORE Width of Id : 10.259765625
BEFORE Width of String1 : 37.845703125
BEFORE Width of String2 : 37.845703125
BEFORE Width of String3 : 37.845703125
BEFORE Width of String4 : 37.845703125
----------
AFTER Width of Text : 88.546875
AFTER Width of Id : 41.0390625
AFTER Width of String1 : 151.3828125
AFTER Width of String2 : 151.3828125
AFTER Width of String3 : 151.3828125
AFTER Width of String4 : 151.3828125
Hope this helps you.
I'm having a problem with TinyMCE 4.6. I've implemented a custom button that bumps the font size of selected text:
ed.addButton('finc', {
image: '/tinymce/plugins/zackel/button_images/big.png',
title: '+ font size',
id : 'finc',
onclick:function(editor,url) {
console.log("************ In finc: ", ed);
var delta;
var currentFontSize = new Number($(ed.selection.getNode()).css('font-size').replace('px',''));
console.log("************ finc: currentFontSize = " + currentFontSize);
var node = ed.selection.getNode(); // <======= LINE 565
var nodeName = node.nodeName; // for example 'DIV ' or 'P'
console.log("************ finc: node is ", node, "nodeName = " + nodeName);
if (currentFontSize >= 24) {
delta = 2;
}
else {
delta = 1;
}
currentFontSize = currentFontSize + delta;
console.log("************ finc: New font size = " + currentFontSize);
ed.formatter.register('incfont', {
inline : 'span',
styles : {'font-size' : currentFontSize + 'px'}
});
ed.formatter.apply('incfont');
console.log("********** finc: posting to val box " + currentFontSize);
$("div#px_val button").text(currentFontSize + 'px'); // show value in value box
}
});
If the text is initially in a P the button works fine but puts the text into a span inside the P when it's done. If I then just hit the button again it fails because the node it brings back on line 565 is still the P, which still has the original font size. So if he initial font size is 16, it goes to 17 but then every bump after that stays at 17. If I deselect the text after bumping it and reselect it, line 565 gets the span and the bumps work every time.
How can I force a reselection from my code, so 565 finds the span the second time instead of the P, without me deselecting and reselecting the text?
Thanks
It seems to me that I understand you problem, but i believe that the text re-selection should not happen every time you apply the formatting - just only in the case TinyMCE is adding the new SPAN.
Here is my proposal:
var delta;
var currentFontSize = new Number($(ed.selection.getNode()).css('font-size').replace('px',''));
var node = ed.selection.getNode();
var nodeName = node.nodeName; // for example 'DIV ' or 'P'
if (currentFontSize >= 24) {
delta = 2;
}
else {
delta = 1;
}
currentFontSize = currentFontSize + delta;
ed.formatter.register('incfont', {
inline : 'span',
styles : {'font-size' : currentFontSize + 'px'}
});
var cnt = ed.selection.getContent({format : 'html'});
var lenBefore = $(cnt).length;
ed.formatter.apply('incfont');
var cnt = ed.selection.getContent({format : 'html'});
var lenAfter = $(cnt).length;
if(lenAfter > lenBefore) {
var newText = ed.selection.selectedRange.startContainer;
var rng = ed.dom.createRng();
rng.setStart(newText, 0);
rng.setEnd(newText, newText.nodeValue.length);
ed.selection.setRng(rng);
ed.nodeChanged();
}
Explanation:
when you apply the formatter for the first time, TinyMCE is adding the SPAN and you will find the new selection inside the ed.selection.selectedRange.startContainer node of type text. This is the same as the first child node of type text of the newly inserted SPAN. For subsequent actions, there shall be no need to do any re-selection.
Moreover, IMHO i feel somehow unusual to change the font size in mouse click, i would prefer a standard plugin button which works only with a already existing text selection (but this is up to you):
Of course, the main question of the re-selection is solved, and the plugin will work repeatedly with subsequent mouse clicks also by using a plugin button.
Just in case, as said before, you may also check at the very top if there is any content:
var hasContent = ed.selection.getContent({format : 'text'}.length > 0);
if(!hasContent) return;
So i believe the whole stuff should do the job but anyway, i feel there is still room for some improvements, for example if you need also to reduce the font size, and thus you will also need to delete the already existing - but no longer necessary - SPAN which contain the formatting.
This is my attempt to write my own layout class for a panel of buttons (which may have between 2 and 20 buttons). Basically they should all be of a uniform size, with a constant spacing (5px) and resize appropriately.
However it doesn't always work.
Sometimes it works absolutely fine, but others it gives space for an extra column, or becomes unable to add additional columns on resizing (removing columns is fine), or something wont work. And it takes ages and seems horribly expensive in terms of computations. Reducing width seems significantly more painful in this respect for some reason.
Anyway, here it is:
package layouts
{
import mx.core.ILayoutElement;
import spark.components.supportClasses.GroupBase;
import spark.layouts.supportClasses.LayoutBase;
public class QButtonsLayout extends LayoutBase
{
public function QButtonsLayout()
{
super();
}
override public function measure():void
{
super.measure();
target.measuredHeight = 130;
}
override public function updateDisplayList(w:Number, h:Number):void
{
super.updateDisplayList(w,h);
var tCount:int = target.numElements; // Number of elements
var tW:Number = target.width; // Width of target (button area) - somewhere between 550 and 1000px
var maxW:Number = 1; // Largest natural width of any given element
var maxH:Number = 1; // Largest natural height of any given element
var eSetW:Number = 1; // Set (to be) width of each element upon the target
var eSetH:Number = 1; // Set (to be) height of each element upon the target
var tCols:Number = 1; // Number of columns upon the target
var tRows:Number = 1; // Number of rows upon the target
for (var i:int = 0; i<tCount; i++) // Find maxW
{
var layoutElement:ILayoutElement = useVirtualLayout ? target.getVirtualElementAt(i):target.getElementAt(i);
var thisW:Number = layoutElement.getPreferredBoundsWidth();
var thisH:Number = layoutElement.getPreferredBoundsHeight();
if(thisW > maxW)
{
maxW = thisW;
};
if(thisH > maxH)
{
maxH = thisH;
};
}
tCols = Math.floor((tW-5)/(maxW+5)); //Find maximum number of columns one can fit onto the target
if(tCols>tCount) //Fix to deal with cases with low tCounts
{
tCols = tCount;
};
tRows = Math.ceil(tCount/tCols); //Find corresponding number of rows
eSetW = ((tW-5)/tCols)-5; //Set widths of elements based upon number of columns, 5s to add some space between elements
eSetH = maxH; //Takes height as the largest height
for (var j:int = 0; j<tCount; j++)
{
var layoutElement2:ILayoutElement = useVirtualLayout ? target.getVirtualElementAt(j):target.getElementAt(j);
var eRow:int = Math.floor(j/tRows); //Row of given element, taking the 1st to be zero
var eCol:int = j - eRow*tRows; // Column of given element, again taking the 1st column as zero
var _x:Number = 5 + eRow*(eSetW+5);
var _y:Number = 5 + eCol*(eSetH+5);
layoutElement2.setLayoutBoundsPosition(_x,_y);
layoutElement2.setLayoutBoundsSize(eSetW,eSetH);
}
}
}
}
Any thoughts would be much appreciated.
Criticism more than welcome.
Turns out that it's not. The layout class itself is fine, as far as calculating element positions and size is concerned.
It is actually a problem in the way in which the buttons used calculated their prefered widths. Whilst I'm not versed in the actual manner in which this happens, it was solved by removing %width values for any height and width values for graphic elements within the button skins. (Eg changing width="100%" to left="0" right="0").
I hope this might help someone, somewhere, sometime.
I've been playing around with different methods of determining at runtime the width of a "label" so that I can resize the "label" because I don't want it to truncate. I've finally found an easy solution through UITextField which allows me to set the .autoSize which is great! However, now I'm trying to "style" (simply adjust font and font size) of the UITextField but it seems that I have to do it manually with '.htmlText' (which I'll gladly accept if that is the ONLY way).
I'm using the .text to set the value of the label.
My test case involves a HBox (I'm actually using a Grid but they should be the same and I've done testing on both):
I style the HBox and the style carries through to the UITextField. I don't believe this will work for me because I have other components inside that I need to style differently.
I've tried: UITextFormat and TextFormat (I see that the .htmlText being updated accordingly but the output doesn't update. Then I noticed that whenever I called hbox.addChild(myUITextField) it would override the .htmlText
I've tried setting the style with myUITextField.setStyle("fontSize", 20) before and/or after the call to addChild neither of which made an impact on the display as per what I noted above.
Changes are being made but they seem to be overrided when I add it to the display.
So what do I need to do in order to style the UITextField aside from manually setting it along with my contents in .htmlText? Solutions not using UITextField is fine as long as there is some easy way of not truncating the text.
EDIT: I want to just do textField.setStyle('fontSize', 20) and expect that every time I change the text, I wouldn't need to use HTML to go with it (so I can just do textField.text = 'something else' and expect that it will still have a font size of 20). This is what I meant by not using .htmlText (sorry if I wasn't clear before).
2nd EDIT: I guess I should present the whole issue and maybe that'll clarify what I did wrong or couldn't achieve.
My intent is to have a Grid and add text into it. I do not want it to wrap or scroll so I add it to the next row in the Grid when the current row's children total width exceeds some number. In order to add it to the next row, I need to be able to calculate the width of the text. I would like to be able to style that text individually based on cases and there might be other components (like a TextInput). Essentially what I'm trying to accomplish is "Fill in the Blank".
I've included code to show what I'm currently doing and it works somewhat. It might be un-related to the original issue of styling but I can't figure out how to adjust the distance between each UITextField but aside from that this fits what I would like to accomplish. Relevant to the question is: I would like to change the way I style each UITextField (currently setting .htmlText) into something a bit straightforward though like I previously mentioned I'll gladly accept using .htmlText if that's the only solution.
So I have a Grid with x Rows in it and in each row, I have exactly one GridItem. Based on the input, I add UITextField and TextInput into the GridItem going on to the next GridItem when necessary. If you have a better way of doing so then that would be better but I guess what I really want is to find a different way of styling.
Also another problem, I'm not sure of the exact way to add a TextField into the display. I tried:
var t : TextField = new TextField();
t.text = "I'm a TextField";
hBox.addChild(t); // doesn't work
//this.addChild(t); // doesn't work either
But I get the following error:
TypeError: Error #1034: Type Coercion failed: cannot convert flash.text::TextField#172c8f9 to mx.core.IUIComponent.
Here's what I have that's working.
private function styleQuestionString(str : String) : String {
return '<FONT leading="1" face="verdana" size="20">' + str + '</FONT>';
}
private function loadQuestion(str : String) : void {
/* Split the string */
var tmp : Array = str.split("_");
/* Track the current width of the GridItem */
var curWidth : int = 0;
/* Display components that we will add */
var txtField : UITextField = null;
var txtInput : TextInput = null;
/* Track the current GridItem */
var curGridItem : GridItem = null;
/* Track the GridItem we can use */
var gridItemAC : ArrayCollection = new ArrayCollection();
var i : int = 0;
/* Grab the first GridItem from each GridRow of Grid */
var tmpChildArray : Array = questionGrid.getChildren();
for (i = 0; i < tmpChildArray.length; i++) {
gridItemAC.addItem((tmpChildArray[i] as GridRow).getChildAt(0));
}
curGridItem = gridItemAC[0];
gridItemAC.removeItemAt(0);
/* Used to set the tab index of the TextInput */
var txtInputCounter : int = 1;
var txtFieldFormat : UITextFormat = new UITextFormat(this.systemManager);
txtFieldFormat.leading = "1";
//var txtFieldFormat : TextFormat = new TextFormat();
//txtFieldFormat.size = 20;
/* Proper Order
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
*/
var txtLineMetrics : TextLineMetrics = null;
var tmpArray : Array = null;
curGridItem.setStyle("leading", "1");
var displayObj : DisplayObject = null;
for (var curItem : int= 0; curItem < tmp.length; curItem++) {
/* Using UITextField because it can be auto-sized! */
/** CORRECT BLOCK (ver 1)
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
***/
tmpArray = (tmp[curItem] as String).split(" ");
for (i = 0; i < tmpArray.length; i++) {
if (tmpArray[i] as String != "") {
txtField = new UITextField();
txtField.htmlText = styleQuestionString(tmpArray[i] as String);
//txtField.setTextFormat(txtFieldFormat); // No impact on output
txtLineMetrics = curGridItem.measureHTMLText(txtField.htmlText);
curWidth += txtLineMetrics.width + 2;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
curGridItem.setStyle("leading", "1");
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Configure the proper gap distance
curWidth = txtLineMetrics.width + 2;
}
displayObj = curGridItem.addChild(txtField);
}
}
//txtField.setColor(0xFF0000); // WORKS
if (curItem != tmp.length - 1) {
txtInput = new TextInput();
txtInput.tabIndex = txtInputCounter;
txtInput.setStyle("fontSize", 12);
txtInputCounter++;
txtInput.setStyle("textAlign", "center");
txtInput.width = TEXT_INPUT_WIDTH;
curWidth += TEXT_INPUT_WIDTH;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Decide if we need to add a buffer
curWidth = TEXT_INPUT_WIDTH + 2;
}
curGridItem.addChild(txtInput);
txtInputAC.addItem(txtInput);
/* Adds event listener so that we can perform dragging into the TextInput */
txtInput.addEventListener(DragEvent.DRAG_ENTER, dragEnterHandler);
txtInput.addEventListener(DragEvent.DRAG_DROP, dragDropHandler);
txtInput.addEventListener(DragEvent.DRAG_EXIT, dragExitHandler);
}
/* Add event so that this label can be dragged */
//txtField.addEventListener(MouseEvent.MOUSE_MOVE, dragThisLabel(event, txtField.text));
}
}
After about 8 hours of searching for a solution to what would seem to be such a simple issue I FINALLY stumbled on your posts here... Thankyou!!!
I have been stumbling around trying to get TextField to work and had no Joy, Label was fine, but limited formatting, and I need to be able to use embedded fonts and rotate. After reading the above this finally worked for me:
var myFormat:TextFormat = new TextFormat();
myFormat.align = "center";
myFormat.font = "myFont";
myFormat.size = 14;
myFormat.color = 0xFFFFFF;
var newTxt:UITextField = new UITextField();
newTxt.text = "HELLO";
addChild(newTxt);
newTxt.validateNow();
newTxt.setTextFormat(myFormat);
The order of addChild before the final 2 steps was critical! (myFont is an embedded font I am using).
One again... a thousand thankyou's...
John
EDIT BASED ON THE ASKERS FEEDBACK:
I didn't realize you wanted to just apply one style to the whole textfield, I thought you wanted to style individual parts. This is even simpler for you, won't give you any trouble at all :)
var textFormat: TextFormat = new TextFormat("Arial", 12, 0xFF0000);
myText.setTextFormat(textFormat);
Be aware that this sets the style to the text that is in the TextField, not necessarily future text you put in. So have your text in the field before you call setTextFormat, and set it again every time you change it just to be sure it stays.
It's probably best if you use a normal TextField as opposed to the component. If you still want the component you may need to call textArea.validateNow() to get it to update with the new style (not 100% sure on that one though) Adobe components are notoriously bad, and should be avoided. :(
To see all available options on the TextFormat object see here
END EDIT ---------
This is easy enough to just do with CSS in a normal old TextField.
var myCSS: String = "Have some CSS here, probably from a loaded file";
var myHTML: String = "Have your HTML text here, and have it use the CSS styles";
// assuming your textfield's name is myText
var styleSheet: StyleSheet = new StyleSheet();
styleSheet.parseCSS(myCSS);
myText.autoSize = TextFieldAutoSize.LEFT;
myText.styleSheet = styleSheet;
myText.htmlText = myHTML;
Supported HTML tags can be found here
Supported CSS can be found here
The reason you have a problem adding Textfield to containers is that it doesn't implement the IUIComponent interface. You need to use UITextField if you want to add it. However, that's presenting me with my own styling issues that brought me to this question.
A few things I know:
TextField is styled using the TextFormat definition, and applying it to the textfield. As Bryan said, order matters.
setStyle does nothing on IUITextField, and the TextFormat method doesn't seem to work the same as in normal TextFields. (Edit #2: Ahah. You need to override the "validateNow" function on UITextFields to use the setTextFormat function)
To autosize a TextArea, you need to do something like this (inheriting from TextArea):
import mx.core.mx_internal;
use namespace mx_internal;
...
super.mx_internal::getTextField().autoSize = TextFieldAutoSize.LEFT;
this.height = super.mx_internal::getTextField().height;
Found this code on, I think, on StackOverflow a while back. Apologies to the original author. But the idea is that you need to access the "mx_internal" raw textfield.
Text and TextArea have wrapping options. (Label does not). So if you set the explicit width of a Text object, you might be able to size using the measuredHeight option and avoid truncation.
(edit: That was #4, but stackoverflow parsed it into a 1...)