Skip to content

Commit 711dfa0

Browse files
birbilis@zoomicon.combirbilis@zoomicon.com
birbilis@zoomicon.com
authored and
birbilis@zoomicon.com
committed
Implemented Snapping logic, Allowing to ctrl/right click parent storyitem (its empty area) to make it active, Fixed GetBackIndex of StoryItem to not count Border when it's not visible (items where getting hidden), fixed GetBackIndex to not count invisible items
1 parent c7f684b commit 711dfa0

File tree

4 files changed

+65
-18
lines changed

4 files changed

+65
-18
lines changed

App/READCOM.App.Globals.dfm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28301,7 +28301,7 @@ object Globals: TGlobals
2830128301
item
2830228302
MultiResBitmap = <
2830328303
item
28304-
Size = 143
28304+
Size = 136
2830528305
end>
2830628306
IconName = 'READ-COM square'
2830728307
SVGText =
@@ -28873,8 +28873,8 @@ object Globals: TGlobals
2887328873
Layers = <
2887428874
item
2887528875
Name = 'READ-COM square'
28876-
SourceRect.Right = 143.000000000000000000
28877-
SourceRect.Bottom = 143.000000000000000000
28876+
SourceRect.Right = 136.000000000000000000
28877+
SourceRect.Bottom = 136.000000000000000000
2887828878
end>
2887928879
end
2888028880
item

App/READCOM.App.Models.pas

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ interface
195195
{Snapping}
196196
function IsSnapping: Boolean;
197197
procedure SetSnapping(const Value: Boolean);
198+
procedure DoSnapping;
198199

199200
{Anchored}
200201
function IsAnchored: Boolean;

App/Views/READCOM.Views.StoryItem.pas

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ TStoryItem = class(TCustomManipulator, IStoryItem, IClipboardEnabled, IStoreab
4747

4848
FOnActiveChanged: TNotifyEvent;
4949

50-
//Global IStory (context) //TODO: talk to that so that we could tell it to open hyperlinks (e.g. http:/...) but also special hyperlinks like story:next, story:previous etc. that can invoke methods to navigate in the story (actually could pass the "verb" to the story itself via special method and it would know how to handle the hyperlinks and the special ones [not have any download and url opening code in the StoryItem])
50+
//Global IStory (context) //TODO: talk to that so that we could tell it to open hyperlinks (e.g. http://...) but also special hyperlinks like story:next, story:previous etc. that can invoke methods to navigate in the story (actually could pass the "verb" to the story itself via special method and it would know how to handle the hyperlinks and the special ones [not have any download and url opening code in the StoryItem])
5151
class var
5252
FStory: IStory;
5353

@@ -166,6 +166,7 @@ TStoryItem = class(TCustomManipulator, IStoryItem, IClipboardEnabled, IStoreab
166166
{Snapping}
167167
function IsSnapping: Boolean; virtual;
168168
procedure SetSnapping(const Value: Boolean); virtual;
169+
procedure DoSnapping; virtual;
169170

170171
{Anchored}
171172
function IsAnchored: Boolean; virtual;
@@ -501,7 +502,8 @@ procedure TStoryItem.PlayRandomAudioStoryItem;
501502

502503
function TStoryItem.GetBackIndex: Integer;
503504
begin
504-
result := inherited + 2; //reserve two more places at the bottom for Glyph and Border
505+
result := inherited + 2; //reserve two more places at the bottom for Background, Glyph
506+
if Border.Visible then inc(result); //reserve one more for Border (if visible)
505507
end;
506508

507509
procedure TStoryItem.SetBackgroundZorder;
@@ -991,6 +993,26 @@ procedure TStoryItem.SetSnapping(const Value: Boolean);
991993
//don't apply snapping at this point, it is supposed to be applied when user drags and drops an unanchored StoryItem into the area of a snapping StoryItem which are both children of the ActiveStoryItem. So we need to know what was dropped to check if there's a snapping sibling's area containing the drop point (comparing in absolute coordinates)
992994
end;
993995

996+
procedure TStoryItem.DoSnapping;
997+
begin
998+
var LParent := ParentStoryItem;
999+
if not Assigned(LParent) then exit;
1000+
1001+
try
1002+
Enabled := false; //disable temporarily
1003+
//Check if our CenterPoint lies inside the bounds of a sibling that has Snapping on
1004+
var LObj := LParent.View.ObjectAtLocalPoint(BoundsRect.CenterPoint, false, false, false, false); //only checking the immediate children (ignoring SubComponents) of our ParentStoryPoint, not checking the disabled ones since we make ourself temporarily disabled to exclude us
1005+
if Assigned(LObj) and (LObj.GetObject is TStoryItem) then
1006+
begin
1007+
var LStoryItemUnderneath := TStoryItem(LObj.GetObject);
1008+
if LStoryItemUnderneath.Snapping then
1009+
Position.Point := LStoryItemUnderneath.BoundsRect.CenterPoint - PointF(Width/2, Height/2); //use same center as the Snapping sibling //Note: should be must faster than BoundsRect := BoundsRect.CenterAt(LStoryItemUnderneath.BoundsRect), at least in Delphi 11.1, where CenterAt calls RectCenter which doesn't seem to be optimized (does 3 OffsetRect operations)
1010+
end;
1011+
finally
1012+
Enabled := true; //make sure we always enable again
1013+
end;
1014+
end;
1015+
9941016
{$endregion}
9951017

9961018
{$region 'Anchored'}
@@ -1132,11 +1154,12 @@ procedure TStoryItem.HandleAreaSelectorMouseDown(Sender: TObject; Button: TMouse
11321154

11331155
if (ssCtrl in Shift) or (ssRight in Shift) then //either Ctrl+LeftClick or just RightClick
11341156
begin
1135-
var LObj := ObjectAtLocalPoint(PointF(X, Y) + AreaSelector.Position.Point, false, true, false, false); //only checking the immediate children (ignoring SubComponents) //TODO: this won't work if we reuse an AreaSelector that belongs to other parent
1157+
var LPoint := PointF(X, Y) + AreaSelector.Position.Point;
1158+
var LObj := ObjectAtLocalPoint(LPoint, false, true, false, false); //only checking the immediate children (ignoring SubComponents) //TODO: this won't work if we reuse an AreaSelector that belongs to other parent
11361159
if Assigned(LObj) and (LObj.GetObject is TStoryItem) then
11371160
TStoryItem(LObj.GetObject).Active := true; //make the ActiveStoryItem
11381161
end;
1139-
end;
1162+
end; //TODO: should also do the extra logic from TStoryPoint.MouseDown that can make the parent storyitem active (maybe make reusable methods)
11401163

11411164
procedure TStoryItem.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: single);
11421165
begin
@@ -1159,6 +1182,8 @@ procedure TStoryItem.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: si
11591182
FDragging := false;
11601183
FDragStart := TPointF.Zero;
11611184

1185+
DoSnapping;
1186+
11621187
ReleaseCapture; //Release capture of mouse actions
11631188
end;
11641189

@@ -1180,28 +1205,48 @@ procedure TStoryItem.MouseMove(Shift: TShiftState; X, Y: Single);
11801205
end;
11811206

11821207
procedure TStoryItem.MouseClick(Button: TMouseButton; Shift: TShiftState; X, Y: Single);
1208+
1209+
function HasActiveChildStoryItem: Boolean;
1210+
begin
1211+
result := (StoryItems.GetFirst( //Note: don't use Contains(ActiveStoryItem), can't compare interfaces for equality (they may both point to same object but be different instances)
1212+
function(StoryItem: IStoryItem):Boolean
1213+
begin
1214+
result := StoryItem.Active;
1215+
end
1216+
) <> nil);
1217+
end;
1218+
11831219
begin
11841220
Shift := FMouseShift; //TODO: remove if Delphi fixes related bug (more at FMouseShift definition)
11851221

11861222
inherited; //fire event handlers
11871223

1188-
if not EditMode then
1224+
if EditMode then //don't use check (Story.StoryMode = TStoryMode.EditMode) aka Global EditMode, in that case would have issue working at a given nesting level (grandchidlren would get in our way, getting activated by accident)
1225+
begin
1226+
if (ssCtrl in Shift) or (ssRight in Shift) then //either Ctrl+LeftClick or just RightClick
1227+
begin
1228+
var LObj := ObjectAtLocalPoint(PointF(X, Y), false, true, false, false); //only checking the immediate children (ignoring SubComponents)
1229+
if Assigned(LObj) and (LObj.GetObject is TStoryItem) then
1230+
TStoryItem(LObj.GetObject).Active := true //make the child under mouse cursor the ActiveStoryItem
1231+
end;
1232+
end
1233+
1234+
else
1235+
1236+
begin
1237+
if ((ssCtrl in Shift) or (ssRight in Shift)) and //either Ctrl+LeftClick or just RightClick
1238+
HasActiveChildStoryItem then //and one of our children is the ActiveStoryItem...
1239+
Active := true //...make us (the parent of the ActiveStoryItem) the Active one (so that we can go back to the parent level by right-clicking it without using the keyboard's ESC key)
1240+
else
11891241
begin
11901242
var LParent := ParentStoryItem;
11911243
if ((FUrlAction <> '') {and //TODO: should have URLs clickable only for children of ActiveStoryItem (and for itself if it's the RootStoryItem maybe) //in non-EditMode should disable HitTest though at everything that isn't the current StoryItem or direct child of the ActiveStoryItem apart from the TextStoryItems maybe (could maybe just disble HitTest at all siblings of ActiveStoryItem and have everything under ActiveStoryItem HitTest-enabled)
11921244
((Assigned(LParent) and LParent.Active) or
11931245
((not Assigned(LParent)) and Active))}) then //only when ParentStoryItem is the ActiveStoryItem //assuming short-circuit evaluation //if no LParent then it's the RootStoryItem, allowing it to have URLAction too
11941246
FStory.OpenUrl(FUrlAction);
1195-
end
1196-
1197-
else //EditMode //TODO: if the StoryItem is not in EditMode but the Story is in EditMode, then make the StoryItem active (else do like below)
1198-
1199-
if (ssCtrl in Shift) or (ssRight in Shift) then //either Ctrl+LeftClick or just RightClick
1200-
begin
1201-
var LObj := ObjectAtLocalPoint(PointF(X, Y), false, true, false, false); //only checking the immediate children (ignoring SubComponents)
1202-
if Assigned(LObj) and (LObj.GetObject is TStoryItem) then
1203-
TStoryItem(LObj.GetObject).Active := true; //make the ActiveStoryItem
12041247
end;
1248+
end;
1249+
12051250
end;
12061251

12071252
{$endregion}

Zoomicon.Manipulation/Zoomicon.Manipulation.FMX.CustomManipulator.pas

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ procedure TCustomManipulator.Loaded;
276276

277277
function TCustomManipulator.GetBackIndex: Integer;
278278
begin
279-
result := inherited + 1; //reserve one more place at the bottom for DropTarget
279+
result := inherited;
280+
if DropTarget.Visible then inc(result); //reserve one more place at the bottom for DropTarget (if visible)
280281
end;
281282

282283
procedure TCustomManipulator.SetDropTargetZorder;

0 commit comments

Comments
 (0)