This is more like a theoretic question: I am curious why the Show method will not also restore the form (like wsNormal).
I know that Show does not set WindowState:
procedure TCustomForm.Show;
begin
Visible := True;
BringToFront;
end;
but why? I mean, I expect the form to actually show up on screen, when I call Show. Obviously, this will not ALWAYS happen. More exactly, it will not happen when the window is minimized.
So, what is the logic behind Show? Why they left out WindowsState?
Visibility and window state are simply independent properties. It is perfectly reasonably that you may wish to change one but not the other.
The design choice made by the VCL designers was to map the underlying Win32 library to the VCL in as fairly direct manner. This means that VCL designers can have flexibility to make their own choices. Were the VCL designed the way that you suggest it would make it much more difficult to change visibility without also changing window state, for example.
We are working with Firemonkey since version 1, and still find difficulties updating components that are currently visible on the screen. There are many 'ways' to request a repaint in Firemonkey, perhaps too many:
Applying a style (ApplyStyle event) - mostly when it becomes visible on screen
Requesting ".repaint" - some components have this
Requesting ".realign" - sometime works, current versions of FMX seem to drop / hide it
Requesting ".invalidate" - some components, mostly forms, have this
Requesting ".invalidateRect" - most components have this
Calling the ApplyStyle event manually - not guaranteed to work and design-wise is incorrect...
Given the lacking documentation for the platform, is there a "well defined" way to change the visual appearance (internal values of a style) when a component is already displayed? I.e. a listboxItem that is already present and visible on screen, a TGrid cell, and so on. Would appreciate your thoughts...
I don't think there's a single unified 'correct way' to refresh the visual of components in Firemonkey.
However, as a recommendation from personal experience, I'd take the .repaint route - this method is at the TControl level, and so it precedes primitives and styled controls (which are generally made up of primitives) and so applies to all visual controls. It also doesn't require parameters, so it's a good all-round solution.
I have noted that Firemonkey has always had an issue correctly repainting controls of it's own accord. Often I'm left with a 'shadow' of the previous visual of a control/primitive, and must call .repaint manually to update it. This got noticeably worse with FM2 and FM3 (presumably due to optimizations made for mobile platforms), and so doing a manual .repaint -- especially where effects such as TGlow and moving or rotating controls/primitives are concerned, has become a standard practice for me.
I like the idea of the Delphi Code Insight window (custom listbox on form without borders basically) where you can click outside of the form and it will close it automatically.
My first thought was to call SetCapture/ReleaseCapture in the FormCreate and FormDestroy respectively. I set the form's caption to X/Y on the FormMouseMove, but it doesn't update the coordinates outside of the form.
If I call SetCapture/ReleaseCapture from within MouseDown and MouseUp, it does update the coordinates as expected, so that proves the concept does work.
I tried other things as well, e.g. post a WM_USER in the OnShow event, and call SetCapture from there, but it still doesn't update the coordinates. I then tried the TApplicationEvents component (OnMessage), but that doesn't work either.
I've read several articles already, but wasn't able to find what I was looking for. Some articles called SetCapture from within the MouseMove, but that doesn't work when the mouse cursor starts outside of the form. Hrm...
The next step would be to use a WindowsHook, but that's where I stopped. I know how to implement one, but it just seems like I'm missing something really obvious here. There must be a simpler way to do this.
Any ideas? :)
Cheers,
Jarno
What you want can be done easier by adding TApplicationEvents to your form and use the event OnDeactivate. That one gets triggered whenever the application loses focus.
Maybe WM_NCHITTEST can help you somehow. As they say, if the mouse is captured, this message is sent to the window that has captured the mouse. So I would capture the mouse for the form and then wait until result of this message is HTNOWHERE, which should mean "out of the window".
But as far as I can remember, this never worked for me fine, so I finally used (as you mentioned) mouse hook.But in my case I've had a lot of components on that popup form and you need to consider messaging for them too.
The implementation I've used (with a little changes) and which works is here.
I had a similar problem (I needed to implement a scrolling windows if the mouse pointer hovered over a special area and I was not able to use SetCapture)
and circumvented it using a timer + GetCursorPos method.
Just do then a ScreenToClient and check if the mouse is within the window.
Question:
Can anyone point to an article or code samples anywhere on how to
provide BOTH editing AND range selection in a TStringGrid?
Yes, I KNOW there are third-party grids that do this, but it's
frustrating that the built-in grid lacks this basic capability.
Background:
It's pretty normal to expect to be able to both edit a cell in a grid,
and also to select a range of cells such as for a Copy operation.
As delivered, TStringGrid doesn't do that. It's either/or. In fact, the
docs tell us about the grid Options, "When goEditing is included in
Options, goRangeSelect has no effect".
However, it looks like it may be possible to do editing and rangeselects
in a TStringGrid anyway!!! Through careful use of the mousedown,
mouseup, selectcell and exit events, you can get dang close by switching
editing elements on and off at the right times. But I still don't have
it perfect, and that only covers mouse use, not keyboard changes.
I have not used the TStringGrid for this, so I can't provide a specific answer. But am I right in assuming you can manually (in code) start a cell being edited? That link implies it is possible even if the grid doesn't have goEditing included in its Options. (See below to work around this if this is not true.)
If so, I'd suggest the following approach:
Combined selection and edit behaviour
I find this is a good, Windows-standard-behaviour sort of approach:
Leave the grid in selection mode, so mouse and keyboard interaction selects cells
Trigger a cell being edited yourself, based on certain criteria (I think you are on the way to doing this from what you said in your last paragraph.) There are common ways to trigger editing, and the following criteria are what my programs follow when they do something similar with other controls:
Selection is normal. Ie, click to select, click and drag to multi-select, use the keyboard arrows and Shift or Control to select, etc.
A cell enters edit mode when either:
A cell is selected and the user presses Enter or F2 (F2 is the standard "Rename" or "Edit" shortcut, which works in a number of programs)
The user "slow-double-clicks" on a cell - ie, slow-double-clicks to select and edit, or clicks again, after a pause, on an already-selected cell. This mimics Explorer's behaviour, where if a file is selected and you later click on it, it enters the inline edit/rename mode. To implement this, record when a cell was last clicked (and selected.) If it is clicked again, and if the time is greater than GetDoubleClickTime then they have clicked twice, slowly, and enter edit mode. This allows you to distinguish between the first click to select, a double-click (to perform some kind of action), and a slow second click, to enter edit mode.
I also tend to check the mouse position, so that if an object is slow-double-clicked and it wasn't first selected (ie, this both selects the object and then enters edit mode) I verify the mouse hasn't moved very much. I use GetSystemMetrics to find the double-click distance, and check that the slow double click was within this box. (Because it's not a true doubleclick, I actually check the distance times 2. My action code is:
const int iMAX_MOVE_AMOUNT = ::GetSystemMetrics(SM_CYDOUBLECLK) * 2; (sorry, C++ not Delphi, but should be convertable easily enough!)
but I'm actually not certain if this is completely and utterly 100% to Windows guidelines. In practice users find it works as they expect, though.)
That should let you change between selecting and editing at the appropriate times with both the keyboard and the mouse.
Miscellaneous thoughts
You may find some of this is cleaner and easier to implement by subclassing TStringGrid and creating a new component. That will allow you to implement this in normal code and override the inbuilt behaviour (rather than event handlers) while keeping it invisible to the form code. It will also give you lower-level access to the mouse events or Windows messages than are exposed simply through events such as OnMouseDown. Finally, if there are problems with showing the editor when goEditing is included in Options, this will allow you to change that behaviour. You could also add your own events if you want your code to respond to certain things happening, such as creating an OnBeginEdit event, say.
Creating your own components is normally regarded as an advanced Delphi topic, but it's actually remarkably easy once you know how! This site has a few good topics that will introduce you to the subject in general, and if you go this route and encounter problems, Stack Overflow is of course a good place to ask questions :) The Embarcadero Delphi » VCL » Writing Components newsgroup / forum is also an excellent resource, in fact possibly even better than SO for this specific topic.
Hope that helps!
Yes it's old post, but the problem still exist on Delphi XE3.
To manage this feature I used next "trick" in SelectCell procedure :
if (ARow = StringGridParam.Row) then
begin
StringGridParam.Options:= StringGridParam.Options + [goEditing] - [goRowSelect];
end else begin
StringGridParam.Options:= StringGridParam.Options + [goRowSelect] - [goEditing];
end;
i know that windowless controls are not magic. A windowless control can have input focus (e.g. Internet Explorer). Input focus is nothing more than drawing either:
a blinking cursor
a dotted line around the perimeter
a slight blue tinge on a button
and when the user begins mashing keys, reacting appropriately. You know the keystrokes are meant for that focused control, because that's the control has focus.
In the case of my (Windows®) window, i would have to know that my windowless child control (let's pretend it's a descendant of TGraphicControl) gets the keyboard events. So during my form's OnKeyDown, OnChar, OnKeyUp, i would need to pretend they are going to my windowless child control.
Which i can do, but it is a pain.
But then the user will probably want to use Tab navigation, and i'll have to somehow intercept Delphi's normal tab control order handling, and hook in myself to say that this thing is the next (and previous) in the tab order.
Which i can do, but it is a pain.
And then there's ActiveControl, which doesn't understand anything except TWinControl's. So if Delphi ever tries to figure out who has focus, it will go insane. So i'd have to have an alternate implementation of ActiveControl.
Which i can do, but it is a pain.
In other words: is this just too much work? i'm fighting eveything that Delphi is, all so i can have a few dozen windowless controls accessible through keyboard input? The Delphi designers never contemplated using interactive windowless controls, and if i try now to work it in, i'll just stuck in the hurtlocker?
Delphi gave me the chance of aiding me willingly, but i have elected the way of pain.
Some further explanation of windowless controls is needed.
Not every control you interact with has to be a windows control. It is quite possible to have focus on, and send keyboard input to, a control that is not a Windows window.
For example, nearly every control you see in an Internet Explorer browser window is a windowless control. In the following screenshot you can see an edit control, which you can type in, and a button which (in this screenshot) has focus:
You can see the dotted focus rectangle, and the button is bluish (which on Windows indicates that it has focus).
If i were to press Spacebar while the Google Search button has focus, it would press the button. The reason this works is because Microsoft wrote an entire widget library of controls. These controls look and feel (almost) exactly like the regular common controls - they are very nearly exact clones of the Windows common controls, right down to the themes being applied.
Mozilla Firefox and Google Chrome also use a widget library of controls. They don't use Microsoft's built-in windowed controls, but instead use a library of graphical, interactive, windowless widgets.
And if you have a suitable development environment, then the windowless widgets work just like "normal" windowed controls. GTK+ is a widget library, and Glade is an IDE that lets you layout controls in that widget library.
i don't know in what development environment Firefox, Chrome, or Blender were created in, but their widgets support windowless controls.
So now onto my question.
Unless i'm mistaken, it appears to me that although Delphi supports a base TControl, (which has width, height, and can paint itself), it cannot receive keyboard focus. It seems to me that Borland never designed Delphi's VCL as a generic widget library. The only evidence i have to support this is that a Form's ActiveControl is a TWinControl:
property ActiveControl: TWinControl;
That doesn't mean that Delphi could be, or must be, limited to windowed controls. The VCL widget library could be extended to support giving focus to windowless controls.
But perhaps Delphi already supports windowless controls, and i just don't realize it? Is there already an established mechanism in Delphi to support giving focus to TControl's? But i'm a reasonably smart guy, and i'm pretty sure Delphi's VCL cannot do what other widget libraries can do.
Which then leads to another question: how much work would be be to subclass forms and such to support it? Is there someone else out there, perhaps someone on TeamB, who's much smarter than i, who has already tried it, and come to the conclusion that it's impossible?
i'm asking now, up front, if trying to add windowless control support is damn near impossible (i.e. futile) - so that i don't spend weeks on it for nothing. i'm trying to draw on the knowledge of a community of Delphi developers.
i'm asking a question.
It's futile to build windowless controls and fit them into Delphi's VCL framework.
You bring up Internet Explorer as an example. But in that case, it's entirely in charge of everything that resides on it. It has its own internal notion of what the active control is, but think about what it looks like from the outside: It's just one giant control. When you ask the OS what has focus, the single browser control has it, no matter which of the browser's subcontrols appears to have focus.
When you press Tab, it looks to the OS as though the browser has simply consumed a tab character, just like edit controls do. Edit controls move the cursor over a few spaces and add tab characters to their internal buffers; browser controls move the cursor to another region of the display.
You're thinking of doing all this on a Delphi TForm. Delphi forms already have a framework for managing the active control and handling keystrokes, and you're going to have to fight it all. If you want windowless controls, go the Internet Explorer route and build your own container control to hold them so that you can remain in charge of everything that happens inside it.
Your container can be a VCL control, but the things you put on it probably can't — they'll still be expecting to use the VCL focus- and keyboard-handling rules. Notice how you can't put ordinary Windows controls in Internet Explorer, either. Anything you put there needs to go through specific ActiveX interfaces. Maybe you'll need interfaces, too, or maybe you can just make your own set of control classes that descend from some special ancestor class you design to work with your container. Don't start with TGraphicControl; it's too entrenched in the VCL to be usable as the basis for your offshoot control library.
It will be a lot of work, but then again, so was Internet Explorer.
Yes, it is futile.
And it's not Delphi's fault, you're just fighting Windows itself.
If you need a control that behaves like a windowed control, use a windowed one.
And you're right, trying to recreate the whole API stack of windowed controls from scratch is a pain.
Yup, you pretty much have it figured out. Using windowless controls means that you lose everything Windows can do to help you. Having more than a couple on a single actual window is pain.
Most of these programs were most likely not originally developed using RAD type tools so had no choice but to re-invent the wheel. One of the largest advantages of Delphi is the deep VCL and 3rd party component support to provide the look you desire.
One technique that I have used with great success to reduce the amount of window handles used in a complex (tax preparation) form based application was to draw the text on a canvas, and moved a single TCustomEdit decendant to the position the user was editing. It was trivial to capture the TAB/Up/Down keys and move the edit to the appropriate position. The challenge we discovered was in drawing a hot rectangle around the mouse hovered field. We ended up with a grid array of TObject, where the array element would be nil (no field), a TLIst (grid contains multiple fields) or a a class that contained our field descriptor. This reduced the amount of range checks we had to perform since it was more likely that the box only contained a single field, or at most 4 fields.
fpGUI Toolkit is an example of what you want. The latest fpGUI code in the source code repository is based on a multi-windowed design. That simple means every widget/component has a window handle, but Windows or Linux does nothing with that window, other that basic notification messages (mouseenter, mouseexit, etc). fpGUI still has full control over where each component goes, if they are focusable, how they look etc. Some widgets/components in fpGUI are non-windowed components too. eg: TfpgScrollbar, TfpgMainMenu, the button in a ComboBox etc.
If you want a true non-windowed version, mean there is only one top-level window that has a window handle, all other widgets/components inside that window doesn't actually exist to the OS (they have no window handles), then fpGUI can help too. The initial design of fpGUI Toolkit was based on such a design. Again, look in the source code repository for the v0.4 branch of code. I that design, fpGUI had to handle absolutely everything, creating mouseenter/mouseleave events, translate co-ordinate systems for container components, handle (fake) component focus states etc... Yes the initial design is a LOT of work, but then you have a very portable framework which can easily be applied to other OSes too.
And yes, fpGUI is fully implemented in the Object Pascal language using the Free Pascal compiler to give me cross-platform support. Currently fpGUI runs on Windows, Linux (32 & 64-bit), Windows Mobile and Embedded Linux (ARM) devices.
I have no idea of what your problem really is, here, but I think this little history may be relevant...
We have an application which fills out a dozen forms. The user may fill out additional forms, and also change values filled out by the application it self.
Now, in our first implementation, we used windowed components for every single input field, so that the fields could receive focus and input. That turned out to be a big problem, because all this windows took a lot of resources.
We now have windowless controls for every input field. That means that all we end up with, is a combined drawing of the form and its input fields. When the user clicks inside the drawing, or uses some keystrokes to move/set focus, we create a new windowed control for the clicked field. When the user moves to the next input field, we destroy the first window, and create a new one. This way we only have one windowed control which again gave us a nice speed improvement.
Again - I have no idea of what you really want to manage. TWinControl is a TWinControl for a reason, but there may be a solution to what you want, what ever that would be...
I think fgGUI may help you out.
Do check its Wiki first.
I think you can use this framework for your application in Delphi as it is written totally in Pascal. Actually it is based on FreePascal ;)
HTH