Skip to content

Commit fe54ac6

Browse files
author
Hristo Hristov
authored
Layout round instead of ceiling (NativeScript#3833)
* Layout round instead of cailing Add helper method to layout module to convert to/from dips to px and measure the native view whiteSpace affects layout added for iOS Fix bug in switch onMeasure implementation Fix bug in cssValueToDevicePixels iOS implementation ActionBar for iOS is measured with AT_MOST modifier * Fix switch measure routine
1 parent f165382 commit fe54ac6

File tree

18 files changed

+162
-330
lines changed

18 files changed

+162
-330
lines changed

tns-core-modules/ui/action-bar/action-bar.ios.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -232,24 +232,22 @@ export class ActionBar extends ActionBarBase {
232232

233233
private _navigationBarHeight: number = 0;
234234
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number) {
235-
let width = layout.getMeasureSpecSize(widthMeasureSpec);
236-
let widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
235+
const width = layout.getMeasureSpecSize(widthMeasureSpec);
236+
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
237237

238-
let height = layout.getMeasureSpecSize(heightMeasureSpec);
239-
let heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
238+
const height = layout.getMeasureSpecSize(heightMeasureSpec);
239+
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
240240

241241
let navBarWidth = 0;
242242
let navBarHeight = 0;
243-
244-
let frame = this.page.frame;
243+
244+
const frame = this.page.frame;
245245
if (frame) {
246246
let navBar: UIView = frame.ios.controller.navigationBar;
247247
if (!navBar.hidden) {
248-
let navBarSize = navBar.sizeThatFits(CGSizeMake(
249-
(widthMode === layout.UNSPECIFIED) ? Number.POSITIVE_INFINITY : width,
250-
(heightMode === layout.UNSPECIFIED) ? Number.POSITIVE_INFINITY : height));
251-
navBarWidth = layout.toDevicePixels(navBarSize.width);
252-
navBarHeight = layout.toDevicePixels(navBarSize.height);
248+
const desiredSize = layout.measureNativeView(navBar, width, widthMode, height, heightMode);
249+
navBarWidth = desiredSize.width;
250+
navBarHeight = desiredSize.height;
253251
}
254252
}
255253

@@ -276,7 +274,7 @@ export class ActionBar extends ActionBarBase {
276274
View.layoutChild(this, this.titleView, 0, 0, right - left, this._navigationBarHeight);
277275
this.actionItems.getItems().forEach((actionItem) => {
278276
if (actionItem.actionView && actionItem.actionView.ios) {
279-
let measuredWidth = actionItem.actionView.getMeasuredWidth(); 
277+
let measuredWidth = actionItem.actionView.getMeasuredWidth();
280278
let measuredHeight = actionItem.actionView.getMeasuredHeight();
281279
View.layoutChild(this, actionItem.actionView, 0, 0, measuredWidth, measuredHeight);
282280
}

tns-core-modules/ui/core/view/view-common.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -572,20 +572,14 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
572572
let measureHeight = 0;
573573

574574
if (child && !child.isCollapsed) {
575-
let width = layout.getMeasureSpecSize(widthMeasureSpec);
576-
let widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
577-
578-
let height = layout.getMeasureSpecSize(heightMeasureSpec);
579-
let heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
580-
581575
child._updateEffectiveLayoutValues(parent);
582576

583577
let style = child.style;
584578
let horizontalMargins = child.effectiveMarginLeft + child.effectiveMarginRight;
585579
let verticalMargins = child.effectiveMarginTop + child.effectiveMarginBottom;
586580

587-
let childWidthMeasureSpec = ViewCommon.getMeasureSpec(width, widthMode, horizontalMargins, child.effectiveWidth, style.horizontalAlignment === "stretch");
588-
let childHeightMeasureSpec = ViewCommon.getMeasureSpec(height, heightMode, verticalMargins, child.effectiveHeight, style.verticalAlignment === "stretch");
581+
let childWidthMeasureSpec = ViewCommon.getMeasureSpec(widthMeasureSpec, horizontalMargins, child.effectiveWidth, style.horizontalAlignment === "stretch");
582+
let childHeightMeasureSpec = ViewCommon.getMeasureSpec(heightMeasureSpec, verticalMargins, child.effectiveHeight, style.verticalAlignment === "stretch");
589583

590584
if (traceEnabled()) {
591585
traceWrite(child.parent + " :measureChild: " + child + " " + layout.measureSpecToString(childWidthMeasureSpec) + ", " + layout.measureSpecToString(childHeightMeasureSpec), traceCategories.Layout);
@@ -599,14 +593,18 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
599593
return { measuredWidth: measureWidth, measuredHeight: measureHeight };
600594
}
601595

602-
private static getMeasureSpec(parentLength: number, parentSpecMode: number, margins: number, childLength: number, stretched: boolean): number {
596+
private static getMeasureSpec(parentSpec: number, margins: number, childLength: number, stretched: boolean): number {
597+
const parentLength = layout.getMeasureSpecSize(parentSpec);
598+
const parentSpecMode = layout.getMeasureSpecMode(parentSpec);
599+
603600
let resultSize: number;
604601
let resultMode: number;
605602

606603
// We want a specific size... let be it.
607604
if (childLength >= 0) {
608605
// If mode !== UNSPECIFIED we take the smaller of parentLength and childLength
609606
// Otherwise we will need to clip the view but this is not possible in all Android API levels.
607+
// TODO: remove Math.min(parentLength, childLength)
610608
resultSize = parentSpecMode === layout.UNSPECIFIED ? childLength : Math.min(parentLength, childLength);
611609
resultMode = layout.EXACTLY;
612610
}

tns-core-modules/ui/core/view/view.android.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -370,9 +370,9 @@ export class View extends ViewCommon {
370370
get [horizontalAlignmentProperty.native](): HorizontalAlignment {
371371
return <HorizontalAlignment>org.nativescript.widgets.ViewHelper.getHorizontalAlignment(this.nativeView);
372372
}
373-
set [horizontalAlignmentProperty.native](value: HorizontalAlignment)  {
374-
const  nativeView = this.nativeView;
375-
const  lp: any = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
373+
set [horizontalAlignmentProperty.native](value: HorizontalAlignment) {
374+
const nativeView = this.nativeView;
375+
const lp: any = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
376376
// Set only if params gravity exists.
377377
if (lp.gravity !== undefined) {
378378
switch (value) {
@@ -397,8 +397,8 @@ export class View extends ViewCommon {
397397
return <VerticalAlignment>org.nativescript.widgets.ViewHelper.getVerticalAlignment(this.nativeView);
398398
}
399399
set [verticalAlignmentProperty.native](value: VerticalAlignment) {
400-
const  nativeView = this.nativeView;
401-
const  lp: any = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
400+
const nativeView = this.nativeView;
401+
const lp: any = nativeView.getLayoutParams() || new org.nativescript.widgets.CommonLayoutParams();
402402
// Set only if params gravity exists.
403403
if (lp.gravity !== undefined) {
404404
switch (value) {
@@ -562,6 +562,7 @@ interface NativePercentLengthPropertyOptions {
562562
setPixels: NativeSetter;
563563
setPercent?: NativeSetter
564564
}
565+
565566
function createNativePercentLengthProperty(options: NativePercentLengthPropertyOptions) {
566567
const { key, auto = 0 } = options;
567568
let setPixels, getPixels, setPercent;
@@ -590,11 +591,11 @@ function createNativePercentLengthProperty(options: NativePercentLengthPropertyO
590591
if (length == "auto") { // tslint:disable-line
591592
setPixels(this.nativeView, auto);
592593
} else if (typeof length === "number") {
593-
setPixels(this.nativeView, Math.ceil(length * layout.getDisplayDensity()));
594+
setPixels(this.nativeView, layout.round(layout.toDevicePixels(length)));
594595
} else if (length.unit == "dip") { // tslint:disable-line
595-
setPixels(this.nativeView, Math.ceil(length.value * layout.getDisplayDensity()));
596+
setPixels(this.nativeView, layout.round(layout.toDevicePixels(length.value)));
596597
} else if (length.unit == "px") { // tslint:disable-line
597-
setPixels(this.nativeView, Math.ceil(length.value));
598+
setPixels(this.nativeView, layout.round(length.value));
598599
} else if (length.unit == "%") { // tslint:disable-line
599600
setPercent(this.nativeView, length.value);
600601
} else {

tns-core-modules/ui/core/view/view.ios.ts

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { Point, View as ViewDefinition } from ".";
33

44
import { ios, Background } from "../../styling/background";
55
import {
6-
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
7-
traceEnabled, traceWrite, traceCategories
6+
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
7+
traceEnabled, traceWrite, traceCategories
88
} from "./view-common";
99

1010
import {
@@ -100,35 +100,26 @@ export class View extends ViewCommon {
100100
}
101101

102102
public onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void {
103-
let view = this.nativeView;
104-
let nativeWidth = 0;
105-
let nativeHeight = 0;
106-
107-
let width = layout.getMeasureSpecSize(widthMeasureSpec);
108-
let widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
103+
const view = this.nativeView;
104+
const width = layout.getMeasureSpecSize(widthMeasureSpec);
105+
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
109106

110-
let height = layout.getMeasureSpecSize(heightMeasureSpec);
111-
let heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
107+
const height = layout.getMeasureSpecSize(heightMeasureSpec);
108+
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
112109

110+
let nativeWidth = 0;
111+
let nativeHeight = 0;
113112
if (view) {
114-
if (widthMode === layout.UNSPECIFIED) {
115-
width = Number.POSITIVE_INFINITY;
116-
}
117-
118-
if (heightMode === layout.UNSPECIFIED) {
119-
height = Number.POSITIVE_INFINITY;
120-
}
121-
122-
let nativeSize = view.sizeThatFits(CGSizeMake(layout.toDeviceIndependentPixels(width), layout.toDeviceIndependentPixels(height)));
123-
nativeWidth = layout.toDevicePixels(nativeSize.width);
124-
nativeHeight = layout.toDevicePixels(nativeSize.height);
113+
const nativeSize = layout.measureNativeView(view, width, widthMode, height, heightMode);
114+
nativeWidth = nativeSize.width;
115+
nativeHeight = nativeSize.height;
125116
}
126117

127-
let measureWidth = Math.max(nativeWidth, this.effectiveMinWidth);
128-
let measureHeight = Math.max(nativeHeight, this.effectiveMinHeight);
118+
const measureWidth = Math.max(nativeWidth, this.effectiveMinWidth);
119+
const measureHeight = Math.max(nativeHeight, this.effectiveMinHeight);
129120

130-
let widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
131-
let heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
121+
const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
122+
const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
132123

133124
this.setMeasuredDimension(widthAndState, heightAndState);
134125
}

tns-core-modules/ui/html-view/html-view.ios.ts

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
HtmlViewBase, View, layout, htmlProperty
2+
HtmlViewBase, View, layout, htmlProperty
33
} from "./html-view-common";
44

55
export * from "./html-view-common";
@@ -30,29 +30,20 @@ export class HtmlView extends HtmlViewBase {
3030
var nativeView = this._nativeView;
3131
if (nativeView) {
3232

33-
let width = layout.getMeasureSpecSize(widthMeasureSpec);
34-
let widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
33+
const width = layout.getMeasureSpecSize(widthMeasureSpec);
34+
const widthMode = layout.getMeasureSpecMode(widthMeasureSpec);
3535

36-
let height = layout.getMeasureSpecSize(heightMeasureSpec);
37-
let heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
36+
const height = layout.getMeasureSpecSize(heightMeasureSpec);
37+
const heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
3838

39-
if (widthMode === layout.UNSPECIFIED) {
40-
width = Number.POSITIVE_INFINITY;
41-
}
39+
const desiredSize = layout.measureNativeView(nativeView, width, widthMode, height, heightMode);
4240

43-
if (heightMode === layout.UNSPECIFIED) {
44-
height = Number.POSITIVE_INFINITY;
45-
}
41+
const labelWidth = Math.min(desiredSize.width, width);
42+
const measureWidth = Math.max(labelWidth, this.effectiveMinWidth);
43+
const measureHeight = Math.max(desiredSize.height, this.effectiveMinHeight);
4644

47-
let nativeSize = nativeView.sizeThatFits(CGSizeMake(width, height));
48-
let labelWidth = layout.toDevicePixels(nativeSize.width);
49-
50-
labelWidth = Math.min(labelWidth, width);
51-
let measureWidth = Math.max(labelWidth, this.effectiveMinWidth);
52-
let measureHeight = Math.max(layout.toDevicePixels(nativeSize.height), this.effectiveMinHeight);
53-
54-
let widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
55-
let heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
45+
const widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
46+
const heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);
5647

5748
this.setMeasuredDimension(widthAndState, heightAndState);
5849
}

tns-core-modules/ui/label/label.ios.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,26 +73,18 @@ export class Label extends TextBase implements LabelDefinition {
7373
let height = layout.getMeasureSpecSize(heightMeasureSpec);
7474
let heightMode = layout.getMeasureSpecMode(heightMeasureSpec);
7575

76-
if (widthMode === layout.UNSPECIFIED) {
77-
width = Number.POSITIVE_INFINITY;
78-
}
79-
80-
if (heightMode === layout.UNSPECIFIED) {
81-
height = Number.POSITIVE_INFINITY;
82-
}
83-
8476
this._fixedSize = (widthMode === layout.EXACTLY ? FixedSize.WIDTH : FixedSize.NONE)
8577
| (heightMode === layout.EXACTLY ? FixedSize.HEIGHT : FixedSize.NONE);
8678

87-
let nativeSize = nativeView.sizeThatFits(CGSizeMake(layout.toDeviceIndependentPixels(width), layout.toDeviceIndependentPixels(height)));
88-
let labelWidth = layout.toDevicePixels(nativeSize.width);
79+
const nativeSize = layout.measureNativeView(nativeView, width, widthMode, height, heightMode);
80+
let labelWidth = nativeSize.width;
8981

9082
if (this.textWrap) {
9183
labelWidth = Math.min(labelWidth, width);
9284
}
9385

9486
let measureWidth = Math.max(labelWidth, this.effectiveMinWidth);
95-
let measureHeight = Math.max(layout.toDevicePixels(nativeSize.height), this.effectiveMinHeight);
87+
let measureHeight = Math.max(nativeSize.height, this.effectiveMinHeight);
9688

9789
let widthAndState = View.resolveSizeAndState(measureWidth, width, widthMode, 0);
9890
let heightAndState = View.resolveSizeAndState(measureHeight, height, heightMode, 0);

tns-core-modules/ui/page/page.ios.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ export class Page extends PageBase {
514514

515515
if (!this._modalParent && this.frame && this.frame._getNavBarVisible(this)) {
516516
// Measure ActionBar with the full height.
517-
let actionBarSize = View.measureChild(this, this.actionBar, widthMeasureSpec, heightMeasureSpec);
517+
let actionBarSize = View.measureChild(this, this.actionBar, widthMeasureSpec, layout.makeMeasureSpec(height, layout.AT_MOST));
518518
actionBarWidth = actionBarSize.measuredWidth;
519519
actionBarHeight = actionBarSize.measuredHeight;
520520
}

tns-core-modules/ui/styling/background.ios.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -145,19 +145,15 @@ function _flipImage(originalImage: UIImage): UIImage {
145145
return flippedImage;
146146
}
147147

148-
function cssValueToDevicePixels(source: string, total: number): number {
149-
let result;
148+
function cssValueToDevicePixels(source: string, totalPx: number): number {
150149
source = source.trim();
151-
152150
if (source.indexOf("px") !== -1) {
153-
result = parseFloat(source.replace("px", ""));
154-
}
155-
else if (source.indexOf("%") !== -1 && total > 0) {
156-
result = (parseFloat(source.replace("%", "")) / 100) * layout.toDeviceIndependentPixels(total);
151+
return parseFloat(source.replace("px", ""));
152+
} else if (source.indexOf("%") !== -1 && totalPx > 0) {
153+
return (parseFloat(source.replace("%", "")) / 100) * totalPx;
157154
} else {
158-
result = parseFloat(source);
155+
return layout.toDevicePixels(parseFloat(source));
159156
}
160-
return layout.toDevicePixels(result);
161157
}
162158

163159
function drawNonUniformBorders(nativeView: NativeView, background: Background) {
@@ -293,10 +289,10 @@ function drawClipPath(nativeView: UIView, background: Background) {
293289
const layerSize = layerBounds.size;
294290

295291
const bounds = {
296-
left: layerOrigin.x,
297-
top: layerOrigin.y,
298-
bottom: layerSize.height,
299-
right: layerSize.width
292+
left: layout.toDevicePixels(layerOrigin.x),
293+
top: layout.toDevicePixels(layerOrigin.y),
294+
bottom: layout.toDevicePixels(layerSize.height),
295+
right: layout.toDevicePixels(layerSize.width)
300296
};
301297

302298
if (bounds.right === 0 || bounds.bottom === 0) {

tns-core-modules/ui/styling/style-properties.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,16 @@ function toDevicePixelsCommon(length: PercentLength, auto: number, parentAvailab
5959
return auto;
6060
}
6161
if (typeof length === "number") {
62-
return Math.ceil(layout.getDisplayDensity() * length);
62+
return layout.round(layout.toDevicePixels(length));
6363
}
6464
switch (length.unit) {
6565
case "px":
66-
return Math.ceil(length.value);
66+
return layout.round(length.value);
6767
case "%":
68-
return Math.round(parentAvailableWidth * length.value);
68+
return layout.round(parentAvailableWidth * length.value);
6969
case "dip":
7070
default:
71-
return Math.ceil(layout.getDisplayDensity() * length.value);
71+
return layout.round(layout.toDevicePixels(length.value));
7272
}
7373
}
7474

tns-core-modules/ui/styling/style/style.d.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,14 +134,14 @@ export class Style extends Observable {
134134
public view: ViewBase;
135135

136136
//flexbox layout properties
137-
flexDirection: FlexDirection;
138-
flexWrap: FlexWrap;
139-
justifyContent: JustifyContent;
140-
alignItems: AlignItems;
141-
alignContent: AlignContent;
142-
order: Order;
143-
flexGrow: FlexGrow;
144-
flexShrink: FlexShrink;
145-
flexWrapBefore: FlexWrapBefore;
146-
alignSelf: AlignSelf;
137+
public flexDirection: FlexDirection;
138+
public flexWrap: FlexWrap;
139+
public justifyContent: JustifyContent;
140+
public alignItems: AlignItems;
141+
public alignContent: AlignContent;
142+
public order: Order;
143+
public flexGrow: FlexGrow;
144+
public flexShrink: FlexShrink;
145+
public flexWrapBefore: FlexWrapBefore;
146+
public alignSelf: AlignSelf;
147147
}

0 commit comments

Comments
 (0)