33 | | As described in the analysis, the resize area will be a thin rectangular area around the border outline. [[BR]] |
34 | | It will be divided into 8 subareas in the following way: |
35 | | * The border outline has 8 interesting points described in the Position enum - TOP_LEFT, MIDDLE_LEFT, etc. |
36 | | * 8 rectangles with centers these points that are twice smaller than the border outline will be created. |
37 | | * Each subarea will be constructed as the intersection of the resize area with one of these rectangles. |
| 33 | As described in the analysis, the resize area will be a thin rectangular area around the border outline and will be divided into 8 subareas.[[BR]] |
| 34 | A new inner class of FrameView will be created to represent them - '''ResizeAreaElement'''. [[BR]] |
| 35 | It will have one important property - the position of the element relative to the border rectangle, i.e. top left, top middle, top right, etc. (described in the Position enumeration).[[BR]] |
| 36 | Using the position we can calculate the responsible area of the element in the following way:[[BR]] |
| 37 | - construct the whole resize area - a rectangular area around the border |
| 38 | {{{ |
| 39 | ImmRect outer = new ImmRect(midBorder.getX() - THRESHOLD, |
| 40 | midBorder.getY() - THRESHOLD, |
| 41 | midBorder.getWidth() + 2 * THRESHOLD, |
| 42 | midBorder.getHeight() + 2 * THRESHOLD); |
| 43 | |
| 44 | ImmRect inner = new ImmRect(midBorder.getX() + THRESHOLD, |
| 45 | midBorder.getY() + THRESHOLD, |
| 46 | midBorder.getWidth() - 2 * THRESHOLD, |
| 47 | midBorder.getHeight() - 2 * THRESHOLD); |
| 48 | |
| 49 | ImmArea totalArea = new ImmArea(outer).subtract(new ImmArea(inner)); |
| 50 | }}} |
| 51 | - keeping an appropriate point from the outer rectangle (according to the position property), construct a new one 3 times smaller than it |
| 52 | {{{ |
| 53 | ImmPoint posPoint = outer.getPoint(position().get()); |
| 54 | |
| 55 | ImmRect zoneGross = new ImmRect(position().get(), posPoint, |
| 56 | new ImmSize (outer.getWidth() / 3, outer.getHeight() / 3)); |
| 57 | }}} |
| 58 | - find the intersection of the two areas constructed above |
41 | | Each subarea changes the size and position of the frame in a different way, so that the opposite one stays in place.[[BR]] |
42 | | Each subarea will have the position used for its construction (from the Position enum) and 2 coefficients - changeX and changeY.[[BR]] |
43 | | changeX could be: |
44 | | * 0 if the resize area does not change the width of the frame |
45 | | * +1 if dragging this area down (increasing x) will make the frame bigger |
46 | | * -1 if dragging this area down will make the frame smaller |
47 | | The same applies for changeY |
| 62 | 8 ResizeAreaElements will be added to FrameView - top left, top middle, top right, middle left, middle right, bottom left, bottom middle and bottom right. [[BR]] |
| 63 | They will also be added as subelements of its root scene element (in FrameView.sceneElement().sceneElement.compute().new DefaultCompositeSceneElement() {...}.setupElements()). It is important that they should be added after the border element, because otherwise it would obscure them. |
49 | | So if we drag some of the resize areas, the code that will resize the frame will look like: |
| 65 | When the user starts dragging, a MouseEvent will be fired and wrapped in an InputEventR3, which will be again wrapped in an EventR3 by SceneInteractionLogic.[[BR]] |
| 66 | A new controller will be created to handle it - '''FrameLogicR3''' in org.sophie2.main.app.commons.frames package.[[BR]] |
| 67 | It will consist of only one operation - RESIZE_START with filter InputEventR3.MOUSE_PRESSED as eventID and FrameView.ResizeAreaElement.class as source class. In the handle method it will create a new MouseCapture that will actually process dragging in the following way: |
| 68 | * receive the coordinates of the resize vector (in scene coordinates) |
| 69 | * convert them to frame coordinates |
| 70 | * according to the position of the resize area calculate some coefficients: changeX, changeY, changeWidth and changeHeight |
| 71 | * calculate the size and location of the frame |
| 72 | * convert the frame location to page coordinates and set the new bounds |
| 73 | In fact, before creating the MouseCapture, we'll save the original bounds and always use them instead of intermediate ones, because otherwise the error accumulated will be too big. |
51 | | {{{ |
52 | | Frame f; // the frame we're resizing |
53 | | ImmRect r; // the border outline |
54 | | RezizeAreSceneElement resizeArea; // the resize subarea |
55 | | ImmPoint startPos; // the initial position of the point we started to drag |
56 | | ImmPoint mousePos; // the current position of the mouse, i.e. we dragged the resize area from startPos to mousePos |
| 75 | In order to be able to convert from frame to page coordinates after resizing, a few '''changes to the rotation''' were necessary. |
| 76 | * In the model rotation will be possible only with center the top left vertex. The rotationPosition and rotationCenter properties will be removed from Frame. [[BR]] |
| 77 | The user may want rotation with a different center and the View will be responsible for the necessary transformations. It turns out that rotation with an arbitrary center is equivalent to rotation around contentLocation with the same angle + translation with the vector (newContentLocation - contentLocation), where newContentLocation is the point where contentLocation is sent by the original rotation. So all we need to do is calculate newContentLocation and set it to Frame.contentLocation and update Frame.rotationAngle. |
| 78 | * rotationPosition and rotationCenter properties will be added to FrameRotationPropertiesHud. |
58 | | float width = r.getWidth() + |
59 | | resizeArea.changeX().get() * (startPos.getX() - mousePos.getX()); |
60 | | float height = r.getHeight() + |
61 | | resizeArea.changeY().get() * (startPos.getY() - mousePos.getY()); |
| 80 | For testing purposes we need to '''add test mode to MouseCapture'''. When in this mode it will not add a listener, but will allow us to execute its methods manually. |
| 81 | * a new boolean field inTestMode (false by default) with getter and setter |
| 82 | * a new MouseCapture field lastInstance with getter |
| 83 | * In the constructor we should check the mode. If in test mode, no listener should be added and the lastInstance field should be set to this (the current instance of MouseCapture |
| 84 | * The methods should move(int, int) and released() should be made public |
63 | | Position positionToKeep = resizeArea.position().get().getOpposite(); |
64 | | ImmPoint pointToKeep = r.getPoint(opposite); |
65 | | |
66 | | f.setSize(BoundMode.MID_BORDER, new ImmSize(width, height)); |
67 | | f.setLocation(BoundMode.MID_BORDER, positionToKeep, pointToKeep); |
68 | | }}} |
69 | | |
70 | | A new class ResizeAreaSceneElement extending DefaultSceneElement with the following fields will be created: |
71 | | * public Prop<ImmRect> borderOutline(); |
72 | | * public Prop<Position> position(); |
73 | | * public Prop<Integer> changeX(); |
74 | | * public Prop<Integer> changeY(); |
75 | | In addition, it will override clip() to construct the clipping rectangle - with center borderOutline().get().getPoint(position().get()) and width and height twice as small as the border outline's width and height. [[BR]] |
76 | | |
77 | | |
78 | | |
79 | | A new ElementHelper for resize area scene elements is needed - ResizeAreaElementHelper [[BR]] |
80 | | In getResponsibleArea() it will construct a rectagular area around the border outline. |
81 | | |
82 | | |
83 | | |
84 | | In FrameView 8 resize scene elements will be added. They will also be added as subelements of FrameView.sceneElement after the border element (the order is important because the resize area should be above the border). |
85 | | |
86 | | |
87 | | A new controller used by FrameView to resize the frame will be created. |
88 | | It will consist of 3 operations: |
89 | | * RESIZE_START |
90 | | It has a filter with eventId = InputEventR3.MOUSE_CLICKED, source class - FrameView.class and tip class - ResizeAreaSceneElement.class [[BR]] |
91 | | In the handle method it should save the current position of the border outline, so that it could be used for resizing later. It is necessary to do so because if we consider only the current state, the error accumulated will be too big. |
92 | | * RESIZE |
93 | | It has a filter with eventId = InputEventR3.MOUSE_DRAGGED, source class - FrameView.class and tip class - ResizeAreaSceneElement.class [[BR]] |
94 | | In the handle method it will actually resize the frame |
95 | | * RESIZE_END |
96 | | It has a filter with eventId = InputEventR3.MOUSE_DRAGGED, source class - FrameView.class and tip class - ResizeAreaSceneElement.class [[BR]] |
97 | | In the handle method it will log a change for the UndoManager, so that the resize action could be undone. |
98 | | |
99 | | |
100 | | = Implementation = |
101 | | ^(Describe and link the implementation results here (from the wiki or the repository).)^ |
102 | | |
103 | | = Testing = |
104 | | ^(Place the testing results here.)^ |
| 86 | Tests: [3222] |