Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented MEditableRectangle #1085

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
173 changes: 118 additions & 55 deletions lib/material/internal/material_movable.flow
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ MMovable2T(manager : MaterialManager, parent : MFocusGroup, m : MMovable, m2t :
origin = extractStruct(m.style, MMovableOrigin(const(Factor(0., 0.)))).origin;

isDragging = make(false);
mouseXY = make(zeroPoint);
mouseXY = extractStruct(m.style, MMousePosition(make(zeroPoint))).point;
mouseXYLocal = make(zeroPoint);
cursorPosition = ref zeroPoint;

Expand Down Expand Up @@ -121,10 +121,12 @@ MResizable2T(manager : MaterialManager, parent : MFocusGroup, m : MResizable, m2
onCreate = extractStruct(style, MResizableOnCreate(minMax.max)).size;
controlOnStart = contains(style, MResizableControlOnStart());
availableWH : DynamicBehaviour<WidthHeight> = make(WidthHeight(100., 100.));

resizableEnabled = extractStruct(style, MResizableEnabled(const(true), const(true)));
multipleHandlesMode = tryExtractStruct(style, MMultipleHandlesMode(const([]), \__ -> TEmpty(), makePoint(), makePoint()));

activeHandle = make(None());
mouseDown = make(false);
mouseXY = make(zeroPoint);
startPoint = ref zeroPoint;
minWH = makeWH();
maxWH = makeWH();
createWH = makeWH();
Expand All @@ -138,52 +140,61 @@ MResizable2T(manager : MaterialManager, parent : MFocusGroup, m : MResizable, m2
)
);

makeDefHandle = \reversed ->
TRectangle(interactiveRectangleStyle, TFixed(10., 10.))
|> (\f2 ->
TGroup2(
f2,
TGraphics(
if (reversed)
[
LineTo(9., 9.),
MoveTo(0., 4.),
LineTo(5., 9.)
]
else
[
MoveTo(0., 9.),
LineTo(9., 0.),
MoveTo(4., 9.),
LineTo(9., 4.)
],
[
StrokeOpacity(1.),
Stroke(black),
FillOpacity(0.)
]
)
)
);

(\p -> {
reversed = p.rtl != controlOnStart;
TDisplay("MResizableContent")
|> (\f ->
TGroup2(
TGroup([
f,
TRectangle(interactiveRectangleStyle, TFixed(10., 10.))
|> (\f2 ->
TGroup2(
f2,
TGraphics(
if (reversed)
[
LineTo(9., 9.),
MoveTo(0., 4.),
LineTo(5., 9.)
]
else
[
MoveTo(0., 9.),
LineTo(9., 0.),
MoveTo(4., 9.),
LineTo(9., 4.)
],
[
StrokeOpacity(1.),
Stroke(black),
FillOpacity(0.)
]
)
)
)
|> (\f2 ->
TInteractive(
[
TMouseDown(mouseDown)
],
f2
)
eitherMap(
multipleHandlesMode,
\mhm ->
SelectTGroup(mhm.handles, \__, __, handle -> {
handleMouseDown = make(false);
TConstruct(
[makeSubscribe2(handleMouseDown, \d -> nextDistinct(activeHandle, if (d) Some(handle) else None()))],
m2t(mhm.materialize(handle), p)
)
|> (\h -> TInteractive([TMouseDown(handleMouseDown)], h))
|> (\h -> if (isHandleRight(handle)) TAlignRight(h) else h)
|> (\h -> if (isHandleVerticalCenter(handle)) TCenterY(h) else h)
|> (\h -> if (isHandleBottom(handle)) TAlignBottom(h) else h)
}),
makeDefHandle(reversed)
|> (\f2 -> TInteractive([TMouseDown(mouseDown)], f2))
|> (\f2 -> TCols2Dir(TFillX(), f2, reversed))
|> (\f2 -> TLines2(TFillY(), f2))
)
// |> (\f2 -> TCursor(MoveCursor, f2))
|> (\f2 -> TCols2Dir(TFillX(), f2, reversed))
|> (\f2 -> TLines2(TFillY(), f2))
|> (\f2 -> TAvailable(f2, TGhost("MResizableContent")))
)
|> (\f2 -> TAvailable(f2, TGhost("MResizableContent"))),
])
)
|> (\f -> TAvailable(f, TSized(availableWH)))
|> (\f -> TInteractive([TMouseXY(mouseXY)], f))
Expand All @@ -194,21 +205,73 @@ MResizable2T(manager : MaterialManager, parent : MFocusGroup, m : MResizable, m2
|> (\f -> TLet("MResizableContent", m2t(m.content, p), f))
|> (\f ->
TConstruct(
[
makeSubscribe(mouseDown, \md -> if (md) addMInteractionItemId(manager, resizableId)),
makeSubscribe2(mouseXY, \xy ->
if (isCurrentMInteractionItemId(manager, resizableId) && getValue(mouseDown)) {
blockOtherMInteractionItemIds(manager, resizableId);
avX = if (reversed) getValue(availableWH).width - xy.x else xy.x;
updateWidthHeight(getValue(minWH), getValue(maxWH), WidthHeight(avX, xy.y))
}
),
\ -> {updateWidthHeight(getValue(minWH), getValue(maxWH), getValue(createWH)); nop},
make3Subscribe(minWH, maxWH, availableWH, updateWidthHeight)
],
concat([
makeSubscribe(fOr(mouseDown, fIsSome(activeHandle)), \md -> if (md) addMInteractionItemId(manager, resizableId)),
\ -> {updateWidthHeight(getValue(minWH), getValue(maxWH), getValue(createWH)); nop},
make3Subscribe(minWH, maxWH, availableWH, updateWidthHeight)
],
eitherMap(multipleHandlesMode,
\mhm -> [
makeSubscribe2(ftransistor(fIsSome(activeHandle), mhm.mousePosition), \mousePos ->
if (isCurrentMInteractionItemId(manager, resizableId)) {
maybeApply(getValue(activeHandle), \handle -> {
blockOtherMInteractionItemIds(manager, resizableId);

isRight = isHandleRight(handle);
isBottom = isHandleBottom(handle);
resEnX = fgetValue(resizableEnabled.x);
resEnY = fgetValue(resizableEnabled.y) && !isHandleVerticalCenter(handle);

prevSize = getValue(availableWH);
prevLT = getValue(mhm.leftTop);
prevPoint = Point(
prevLT.x + if (isRight) prevSize.width else 0.,
prevLT.y + if (isBottom) prevSize.height else 0.
);
dx = if (resEnX) (mousePos.x - prevPoint.x) * (if (isRight) 1. else -1.) else 0.;
dy = if (resEnY) (mousePos.y - prevPoint.y) * (if (isBottom) 1. else -1.) else 0.;
updateWidthHeight(getValue(minWH), getValue(maxWH), WidthHeight(prevSize.width + dx, prevSize.height + dy));

newSize = getValue(availableWH);
newX =
if (!resEnX) prevLT.x
else if (isRight) max(prevLT.x, mousePos.x - newSize.width)
else min(prevLT.x + prevSize.width, mousePos.x + newSize.width) - newSize.width;
newY =
if (!resEnY) prevLT.y
else if (isBottom) max(prevLT.y, mousePos.y - newSize.height)
else min(prevLT.y + prevSize.height, mousePos.y + newSize.height) - newSize.height;
nextDistinct(mhm.leftTop, Point(newX, newY));
})
}
)
],
[
makeSubscribe2(mouseXY, \xy ->
if (isCurrentMInteractionItemId(manager, resizableId) && getValue(mouseDown)) {
blockOtherMInteractionItemIds(manager, resizableId);
avX = if (reversed) getValue(availableWH).width - xy.x else xy.x;
updateWidthHeight(getValue(minWH), getValue(maxWH), WidthHeight(avX, xy.y))
}
)
]
)
),
f
)
)
})
|> (\f -> MComponentGroup2T(manager, parent, "MMovable", [IScriptBehaviour("available", availableWH, ISInputValue([], serialize(getValue(availableWH)), true))], f))
|> (\f -> MComponentGroup2T(manager, parent, "MResizable", [IScriptBehaviour("available", availableWH, ISInputValue([], serialize(getValue(availableWH)), true))], f))
}

isHandleRight(handle : MResizableHandle) -> bool {
handle == MTopRight() || handle == MBottomRight() || handle == MCenterRight()
}

isHandleVerticalCenter(handle : MResizableHandle) -> bool {
handle == MCenterLeft() || handle == MCenterRight()
}

isHandleBottom(handle : MResizableHandle) -> bool {
handle == MBottomLeft() || handle == MBottomRight()
}
1 change: 1 addition & 0 deletions lib/material/internal/material_moving_point.flow
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export {
aspectRatio : Maybe<double>,
moveByIcon : bool
) -> Material;
movingMIcon(icon : string, iconSize : double, colorFn : (int) -> MColor, isActive : DynamicBehaviour<bool>) -> Material;
defaultRestictorM(point : Point) -> Point {
point;
}
Expand Down
19 changes: 17 additions & 2 deletions lib/material/material.flow
Original file line number Diff line number Diff line change
Expand Up @@ -2292,7 +2292,8 @@ export {
MDropSpot2(onHover : DynamicBehaviour<int>, onDrop : DynamicBehaviour<int>, dropTropic : Material, handle : bool);

MMovable(target : Material, movableArea : Material, style : [MMovableStyle]);
MMovableStyle ::= MMovablePosition, MMovableExpandArea, MMovableGrabArea, MMovableOrigin, MEnabled, MInteractionsOnTop, MMovableDontPassClicks, MMovableCustomCursor;
MMovableStyle ::= MMovablePosition, MMovableExpandArea, MMovableGrabArea, MMovableOrigin, MEnabled, MInteractionsOnTop,
MMovableDontPassClicks, MMovableCustomCursor, MMousePosition;
MMovablePosition(position : DynamicBehaviour<Point>);
// The target can be moved partially out of the area
MMovableExpandArea();
Expand All @@ -2313,10 +2314,24 @@ export {
MPictureTileMode(); // Tiles available box by a picture, MPictureSize affects tile size

MResizable(content : Material, style : [MResizableStyle]);
MResizableStyle ::= MResizableMinMax, MResizableOnCreate, MResizableControlOnStart;
MResizableStyle ::= MResizableEnabled, MResizableMinMax, MResizableOnCreate, MResizableControlOnStart, MMultipleHandlesMode;
MResizableEnabled(x : Transform<bool>, y : Transform<bool>);
MResizableMinMax(min : Material, max : Material);
MResizableOnCreate(size : Material);
MResizableControlOnStart();
MMultipleHandlesMode(
handles : Transform<[MResizableHandle]>,
materialize : (position : MResizableHandle) -> Material,
leftTop : DynamicBehaviour<Point>,
mousePosition : DynamicBehaviour<Point>
);
MResizableHandle ::= MTopLeft, MTopRight, MCenterLeft, MCenterRight, MBottomRight, MBottomLeft;
MTopLeft();
MTopRight();
MCenterLeft();
MCenterRight();
MBottomRight();
MBottomLeft();

// Scales available space
MScaleAvailable(factor : Transform<Factor>, m : Material);
Expand Down
55 changes: 55 additions & 0 deletions lib/material/material_ui.flow
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import material/material_dialog;
import material/material_print;
import material/material_snackbar;
import material/internal/material_reorder;
import material/internal/material_moving_point;
import ui/animate/teasing;
import form/realhtmlworkaround;
import net/integration_url_parameter;
Expand Down Expand Up @@ -167,6 +168,17 @@ export {
// You can observe the font, but not change it.
MEllipsisTextCurrentFont(fontB : DynamicBehaviour<MFontStyle>);

// Helps to define box size and position from the UI. Transparent by default.
MEditableRectangle(
position : DynamicBehaviour<Point>,
size : DynamicBehaviour<WidthHeight>,
handles : Transform<[MResizableHandle]>,
style : [MEditableRectangleStyle]
) -> Material;

MEditableRectangleStyle ::= MThemeColor, FillOpacity, MCustomHandle, MResizableOnCreate, MResizableEnabled, MResizableMinMax;
MCustomHandle(fn : (MResizableHandle) -> Material);

// Construct an HTML stage with the given metrics
MHTMLStage(wh : Transform<WidthHeight>, constructor : (stage : native) -> () -> void) -> Material;

Expand Down Expand Up @@ -1359,6 +1371,49 @@ MEllipsisTextTwoFonts(
|> (\m -> MAttachAvailableWidth(m, avWidth))
}

MEditableRectangle(
position : DynamicBehaviour<Point>,
size : DynamicBehaviour<WidthHeight>,
handles : Transform<[MResizableHandle]>,
style : [MEditableRectangleStyle]
) -> Material {
handleSize = 24.;
mousePosition = makePoint();
isHovered = make(false);
defHandleFn = \__ -> MVisible(isHovered, MCursor(FingerCursor(), movingMIcon("casino", handleSize, MYellowA, make(false))));
handleFn = extractStruct(style, MCustomHandle(defHandleFn)).fn;
backgroundColor = extractStruct(style, MBlack());
backgroundOpacity = extractStruct(style, FillOpacity(0.));
onCreate = extractStruct(style, MResizableOnCreate(TSized(cloneBehaviour(size))));
minMax = extractStruct(style, MResizableMinMax(TEmpty(), TFillXY()));
resizableEnabled = extractStruct(style, MResizableEnabled(const(true), const(true)));

MMovable(
MResizable(
MGetFocusGroup(\par -> MFrame(0., 0., [MThemeFill(par, backgroundColor), backgroundOpacity], TFillXY())),
[
onCreate,
resizableEnabled,
MResizableMinMax(
MGroup2(minMax.min, TSizedWidthHeight(
fif(resizableEnabled.x, const(2.* handleSize), zero),
fif(resizableEnabled.y, const(2.* handleSize), zero)
)),
minMax.max
),
MMultipleHandlesMode(handles, handleFn, position, mousePosition)
]
)
|> (\m -> MComponent([THovering(isHovered), MPassClicks(), MFocusEnabled(const(false)), MCursorShape(const(ArrowCursor()))], \__ -> m))
|> (\m -> MAttachBox(m, size)),
TFillXY(),
[
MMovablePosition(position),
MMousePosition(mousePosition),
]
)
}

MHTMLStage(wh : Transform<WidthHeight>, constructor : (stage : native) -> () -> void) -> Material {
MComponentGroup(
THTMLStage(wh, constructor),
Expand Down
46 changes: 46 additions & 0 deletions lib/material/tests/test_editable_rectangle.flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import material/material_ui;

main() {
manager = makeMaterialManager([]);
setRendererType("html");

leftTop = make(Point(300., 100.));
fpprint("leftTop")(leftTop);
size = make(WidthHeight(200., 200.));
fpprint("size")(size);

text = make("aaaaa");
textHeight = make(0.);
fpprint("textHeight")(textHeight);

selectedOption = make(0);

content = MGroup([
MFrame(0., 0., [MFill(MBlue(200))], TFillXY()),
MEditableRectangle(leftTop, size,
fselectLift(selectedOption, \opt ->
if (opt == 0) [MTopLeft(), MTopRight()]
else if (opt == 1) [MCenterLeft(), MCenterRight()]
else [MBottomLeft(), MBottomRight()]
),
[
MPurple(200),
FillOpacity(1.),
MResizableEnabled(const(true), const(false)),
MResizableMinMax(TSizedHeight(0., textHeight), TFillXHT(textHeight))
]
),
MTranslate(leftTop,
MAttachHeight(
MAvailable(MDynamicParagraph(text, []), TSized(size)),
textHeight
)
),
MLines2(
MTextInput(text, [], []),
MRadios(selectedOption, [], [MText("TOP", []), MText("CENTER", []), MText("BOTTOM", [])]) |> MCols
)
]);

mrender(manager, true, content);
}