move TRectangle with mouse (FMX, Win32) - firemonkey

I have an FMX form with a TLayout on it aligned to client. On the TLayout I have a TRectangle. I can move the TRectangle easily with the following code in a button click event:
Rectangle1->Position->X = Rectangle1->Position->X + 10;
Is there a clean way for me to do this (move the rectangle) with the mouse? Like click on the Rectangle and move it around to a new location? I'm just playing around trying to make a little drawing program to learn....
Using C++Builder 10.2 Version 25.0.29899.2631 and building in Win32.
UPDATE: I took Hans approach and ended up making it work nicely. I've added the full code as an answer below. Yay!

A way to drag components is to store the offset between the mouse position and the control position on mouse down, then use this offset to calculate the position of the control in the mouse move event.
In semi-pseudo code it would look like this:
Add the following to your TForm class:
fMouseIsDown: boolean;
fMouseDownOffset: TPointF;
procedure OnRectangleMouseDown(X,Y)
begin
fMouseIsDown := true;
fMouseDownOffset := PointF(Rectangle.Position.X-X, Rectangle.Position.Y-Y)
end;
procedure OnRectangleMouseMove(X,Y)
begin
if fMouseIsDown then
begin
Rectangle.Position.X := X+fMouseDownOffset.X;
Rectangle.Position.Y := Y+fMouseDownOffset.Y;
end;
end;
procedure OnRectangleMouseUp(X,Y);
begin
fMouseIsDown := false;
end;

Here is the complete code needed to left-click on and move a TRectangle on an FMX form in Win32 (haven't tried it on mobile yet). Just create a new FireMonkey multi-device application and put a TRectangle and a TButton on it.
Code to add to the forms's class declaration (in the .h file just under class TForm1 : public TForm {):
bool fMouseIsDown; // gets set to TRUE when left mouse click on the rectangle
TPointF fMousePosGlobal; // this is the mouses position relative to the screen
TPointF fMousePosForm; // this is the mouse pos relative to the form
TPointF captionOffset; // this is a small offset necessary since the form's TOP and LEFT are outside of form's client area due to caption bar and left edge of form
TPointF fMouseInRectAtClick; // this is the mouse pos relative to the rectangle (top left of rectangle is 0,0)
Code to add to the rectangle's Rectangle1MouseDown event:
if (Button == 0) { // 0 for left mb, 1 for right mb
fMouseIsDown = true;
fMouseInRectAtClick.X = X; //mouse pos with respect to rectangle at time of click
fMouseInRectAtClick.Y = Y;
}
Code to add to the rectangle's Rectangle1MouseMove event (add to the form's FormMouseMove too or sometimes you lose the rectangle on a fast drag):
fMousePosGlobal = Screen->MousePos(); //mouse global pos
fMousePosForm.X = fMousePosGlobal.X - Form1->Left; // mouse pos relative to the form
fMousePosForm.Y = fMousePosGlobal.Y - Form1->Top;
if (fMouseIsDown) {
Form1->Rectangle1->Position->X = fMousePosForm.X - captionOffset.X - fMouseInRectAtClick.X;
Form1->Rectangle1->Position->Y = fMousePosForm.Y - captionOffset.Y - fMouseInRectAtClick.Y;
}
Code to add to the Rectangle1MouseUp event:
fMouseIsDown = false; // add this to the form's MouseUp too in case you "lose" the rectangle on a drag. That only happened when i forget to set the offsets.
Code to add to the Button's Button1Click event:
captionOffset.X = 8.0; // this accounts for the width of the form left edge
captionOffset.Y = 30.0; // this accounts for the height of the form caption
// if you don't add this your "drag point" on the rectangle will jump as soon as you start the drag.
Thanks to Hans for the direction to start!
Also, i noticed the drag wasn't smooth when moving across other controls. If this bothers you then you need to set those other controls "HitTest" false so they ignore it. Add TEdit boxes if you want to see all the TPointF coordinates as you move mouse and rectangle - helps a bunch when trying to figure out coordinates.

Related

Why does win.setPosition('center') move the window to top-left?

I have this code which I execute after toggling fullscreen back from fullscreen to windowed mode:
win.setPosition('center');
The window consistently appears in top-left part of the screen -- not the middle.
The manual claims at http://docs.nwjs.io/en/latest/References/Window/#winsetpositionposition :
position {String} the position of the window. There are three valid positions: null or center or mouse
Move window to specified position. Currently only center is supported on all platforms, which will put window in the middle of the screen.
I tried "center" both quoted and unquoted, and even "centre" in case it was a typo in the manual.
For the record, the "mouse" value (which I don't want/need) also doesn't work; it seems to put the window in a random or semi-random place, far away from the cursor.
Windows 10. nwjs-v0.61.0-win-x64.
Make sure you are creating the win variable like below:
<script>
const win = nw.Window.get();
// Move window to top left of screen
win.moveTo(0, 0);
// Move window to middle of screen
win.setPosition('center');
// Manually move window to middle of screen
const screen = nw.Screens.screen[0].bounds;
const x = Math.floor((screen.width - win.width) / 2);
const y = Math.floor((screen.height - win.height) / 2);
win.moveTo(x, y);
</script>
You can use nw.Screen.screens to see dimensions of all the monitors if you wanted to move it to a different location.
https://nwjs.readthedocs.io/en/latest/References/Screen#screenscreens
https://nwjs.readthedocs.io/en/latest/References/Window/#winmovetox-y
https://nwjs.readthedocs.io/en/latest/References/Window/#winsetpositionposition

Pass gestures to children

I have an app with a game board and can pick up and move tiles around with panning using mr.gestures, that's working well.
My game board is a MR.Gestures.AbsoluteLayout and that's where I capture the panning gesture.
If I add that game board as a child to another MR.Gestures.AbsoluteLayout then it seems the gestures are blocked by the parent and it no longer works.
Is there a way to pass gestures though to children or ignore gestures on a parent in some way?
Is there a way to pass gestures though to children or ignore gestures
on a parent in some way?
For me this problem appeared only on Android.. The answer is Yes:
When parent receives gesture event it must check if finger x,y is within specific childs views. If yes, parent just ignores the gesture.
Regarding the code, ill just past one event handler to get the idea. In my case i have 2 childs (like and bookmark) over a parent frame:
//-------------------------------------------------------------------
private void OnTapped_MainFrame(object sender, TapEventArgs e)
//-------------------------------------------------------------------
{
//Get parent screen abs pos in pixels
//We are using native code get absolute screen position
var ptFrame = DependencyService.Get<INiftyHelpers>().GetViewAbsolutePosition((View)sender);
//Gets childs (hotspots) screen abs position in pixels
var ptFav = DependencyService.Get<INiftyHelpers>().GetViewAbsolutePosition((View)hsFav);
var ptLike = DependencyService.Get<INiftyHelpers>().GetViewAbsolutePosition((View)hsLike);
//Gets childs (hotspots) rectangles, everything in pixels using screen density
var rectFav = new Rectangle(ptFav, new Size(hsFav.Width * AppHelper.DisplayDensity, hsFav.Height * AppHelper.DisplayDensity));
var rectLike = new Rectangle(ptLike, new Size(hsLike.Width * AppHelper.DisplayDensity, hsLike.Height * AppHelper.DisplayDensity));
//Convert the finger XY to screen pos in pixels
var ptTouch = new Point(ptFrame.X + e.Center.X * AppHelper.DisplayDensity, ptFrame.Y + e.Center.Y * AppHelper.DisplayDensity); //absolute relative to screen
//check if withing childs
if (rectFav.Contains(ptTouch) || rectLike.Contains(ptTouch))
return; //Ignore input and let childs process their gestures!
//Process input
//..
}

How do I center a textbox inside a panel in visual studio?

When I set the textbox's anchor to top,bottom,left,right the textbox still stays on the top. I don't understand, this seems to work for labels and buttons. I kinda need this anchor property for it to adapt well to a form resize.
I'm sorry if I'm missing something incredibly obvious.
If I understand your question, you want a single line textbox to appear centered vertically and horizontally in your form. Still without showing more than one line. In this case you could set just the Anchor Left and Right property to have you textbox centered horizontally on the form, but for the vertical position you need to work with code adjusting the Iop property of the textbox in the Resize event of the form
For example, after the call to InitializeCompoment of your form you could add the following code to center your textbox (C# but easily convertible to VB.NET)
textBox1.Left = 10;
textBox1.Width = yourForm.ClientSize.Width - 20;
textBox1.Top = (yourForm.ClientSize.Height / 2) - yourForm.Height / 2;
textBox1.Anchor = AnchorStyles.Left | AnchorStyles.Right;
and then add the event handler for the resize event of your form where you could call again the vertical repositioning of the textbox
protected void yourForm_Resize(object sender, EventArgs e)
{
textBox1.Top = (this.ClientSize.Height / 2) - textBox1.Height / 2;
}

Get the Absolute Origin of an Object when DragStartedEvent fired

I've an app which like the image shown below (sorry I used an iPhone like mock-ups)
The app has an image as background (displayed using Canvas which takes the whole visual screen size), then another shape (the red rectangle in this case) will shown above the background which can be dragged and pinch zoomed.
Now the question is:
How can I get the coordinates (origins from top-left corner of the screen, i.e. top-left of the canvas) of the top-left corner of the rectangle?
UPDATE
According to #Will's suggestion, I currently move the rectangle in this case via DragDelta using TranslateTransform inside it, like:
rectTransform.X += e.HorizonalChange;
rectTransform.Y += e.VerticalChange;
The rectangle is defined within code, not in XAML:
rect.Stroke = new SolidColorBrush(Colors.Green);
rect.StrokeThickness = 10;
rect.Fill = new SolidColorBrush(Colors.Transparent);
rect.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
rect.VerticalAlignment = System.Windows.VerticalAlignment.Center;
rect.Width = 200;
rect.Height = 200;
canvas.Children.Add(rect);
and the canvas is defined within XAML.
What I've tried:
Initially I was trying to get the start point when the Drag event begins, but by using the DragStarted(object sender, DragStartedGestureEventArgs e), I am only able to output the coords of the point which was touched, but not the point of top-left corner of this rectangle.
And it's similar to the DragCompleted event which will return me the point the gesture ends.
So is there any chance I can get the origin coords of the red rectangle?
I spent nearly an afternoon on Google as well as MSDN and then finally come to find this on SO:
How to get the position of an element in a StackPanel?
which enlightened me using the similar method. In that case, they managed to get the absolute coordinates of an UI Element. Similarily, in my case, I intend to know the absolute origin(coordinates) of the red rectangle to the background canvas.
I use:
GeneralTransform gt = canvas.TransformToVisual(rect);
Point currentPos = gt.Transform(new Point(0, 0));
Then the currentPos is the current position of rectangle (its an absolute position, relative to the canvas).
And Here's the reference of TransformToVisual in MSDN
There's only one thing to pay attention to: the value of X and Y are negative.
Not 100% sure of the syntax here, but in WPF (which should be analogous) I'd handle it by
private double _startX, startY, _endX, _endY;
private void DragStarted(object sender, DragStartedGestureEventArgs e)
{
var rect = sender as UIElement; // I'm assuming the sender is your Rectangle
this._startX = Canvas.GetLeft(rect);
this._startY = Canvas.GetTop(rect);
}
private void DragCompleted(object sender, DragCompletedGestureOrWhateverLolEventArgs e)
{
var rect = sender as UIElement; // I'm assuming the sender is your Rectangle
this._endX = Canvas.GetLeft(rect);
this._endY = Canvas.GetTop(rect);
}
What you say? Not type safe? Yeah, well, okay. How about give your rectangle an x:Name and then use that in these methods?
<Rectangle x:Name="rect" SkipOtherPropertiesLol="true" />
and
this._startX = Canvas.GetLeft(rect); //etc
Attached properties are a little confusing at first, but they are very powerful and easy to use once you understand their fairy magic.

Set SWT Check/Radio Button Foreground color in Windows

This is not a duplicate of How to set SWT button foreground color?. It's more like a follow up. I wrote follow-up questions as comments, but did not get any responses, so I thought I'd try to put it up as a question, and hopefully some expert will see it.
As is pointed in the referenced question, windows native button widgets do not support setting the foreground color (in fact, after more further research (more like experiments), it's been revealed that setForeground() works under the Classic Theme, but not others).
The answer/suggestion given in the referenced question is a good one (a.k.a providing a paint listener and drawing over the text with the correct color). I gave it a whirl but ran into a world of problems trying to decide the coordinate at which to draw the text:
It appears that - in addition to SWT attributes like alignment etc. - Windows has some rather hard-to-figure-out rule of deciding the location of the text. What makes it worse is that the location appears to be dependent on the windows theme in effect. Since I need to draw the text exactly over the natively-drawn windows text in order to override the color, this is a huge problem.
Please, can someone provide some much-needed help here? It'd be greatly appreciated!
Thank you!
On the same PaintListener you use to paint the coloured background, you have to calculate the position and draw the text. Here's how we do it here:
public void paintControl( PaintEvent event ) {
// Is the button enabled?
if ( !isEnabled() ) {
return;
}
// Get button bounds.
Button button = (Button)event.widget;
int buttonWidth = button.getSize().x;
int buttonHeight = button.getSize().y;
// Get text bounds.
int textWidth = event.gc.textExtent( getText() ).x;
int textHeight = event.gc.textExtent( getText() ).y;
// Calculate text coordinates.
int textX = ( ( buttonWidth - textWidth ) / 2 );
int textY = ( ( buttonHeight - textHeight ) / 2 );
/*
* If the mouse is clicked and is over the button, i.e. the button is 'down', the text must be
* moved a bit down and left.
* To control this, we add a MouseListener and a MouseMoveListener on our button.
* On the MouseListener, we change the mouseDown flag on the mouseDown and mouseUp methods.
* On the MouseMoveListener, we change the mouseOver flag on the mouseMove method.
*/
if ( mouseDown && mouseOver ) {
textX++;
textY++;
}
// Draw the new text.
event.gc.drawText( getText(), textX, textY );
// If button has focus, draw the dotted border on it.
if ( isFocusControl() ) {
int[] dashes = { 1, 1 };
evento.gc.setLineDash( dashes );
evento.gc.drawRectangle( 3, 3, buttonWidth - 8, buttonHeight - 8 );
}
}
In the end, I decided to implement it as a custom Composite with a checkbox/radio button and a label. Not ideal, but I'll have to make do.

Resources