Change color of Button Text

Hi,
How can I change the color of CustomTextColor method of een button when I move the cursor over the button text?

Change this Castle Game Engine: CastleControls: Class TCastleButton ?

Or perhaps use this Castle Game Engine: CastleControls: Class TCastleButton ?

Perhaps, use it here Castle Game Engine: CastleUIControls: Class TCastleUserInterface

And here Castle Game Engine: CastleUIControls: Class TCastleUserInterface

Thanks for your reply.

Yeah, I read the API but don’t understand it quite without examples.
After some attempts with ChatGPT I got this

procedure MouseMove(const Sender: TObject; const Event: TInputMotion;
  const Position: TVector2);
begin

  if ViewMain.OptionButton1.RenderRect.Contains(Position) then
    ViewMain.OptionButton1.CustomTextColor := Yellow
  else
    ViewMain.OptionButton1.CustomTextColor := White;
end;                    

But when I call it with

ViewMain.OptionButton1.OnInternalMouseEnter:= @MouseMove;

I got this error:

Any idea what is wrong here?

what is the text of the error? pictures are very hard to read

Error: Incompatible types: got “<address of procedure(const TObject;const TInputMotion;const TGenericVector2);Register>”expected “procedure variable type of procedure(const TCastleUserInterface) of object;Register>”

Well that is exactly what it says, you try to assign a varible as wrong, incompatible data type.

You have to provide the different procedure and FPC even told you the signature.

You have to create the

procedure TSomeMyObject.SomeProcName(const TCastleUserInterface);

And that procedure you would probably be able to assign to that variable.

https://www.freepascal.org/docs-html/ref/refse17.html
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Procedural_Types_(Delphi)

Thanks for the links but I have no clue. Even chatGPT repeatedly makes a pointer that does not work here:

procedure MouseMove(Sender: TObject; const Event: TInputMotion;
  const Position: TVector2);
begin
  if Sender is TCastleButton then
  begin
    if (Position.X >= TCastleButton(Sender).RenderRect.Left) and
       (Position.X <= TCastleButton(Sender).RenderRect.Right) and
       (Position.Y >= TCastleButton(Sender).RenderRect.Bottom) and
       (Position.Y <= TCastleButton(Sender).RenderRect.Top) then
      TCastleButton(Sender).CustomTextColor := Yellow
    else
      TCastleButton(Sender).CustomTextColor := White;
  end;
end;              
OptionButton1.OnInternalMouseEnter := @MouseMove; 

The error Incompatible types: got “<address of procedure(const TObject;const TInputMotion;const TGenericVector2);Register>”expected “procedure variable type of procedure(const TCastleUserInterface) of object;Register>” is when this pointer is used.

chatGPT knows no sense and hence can make no sense, they are just statistical tools, data-miners (to use previous buzz word about it), “even chatGPT” sounds like “even the butterfly on the dandellion out my door” - it is normal they have no clue

You have to learn language basics.

  1. Data types
  2. in particular, procedural data types
  3. object-oriented design
  4. in particular, “object methods” or “class methods” as a special kind of a procedure, and “procedure (…) of object” as a special kind of procedural type

You have to provide the different procedure and FPC even told you the signature.
You have to create the
procedure TSomeMyObject.SomeProcName(const TCastleUserInterface);

See also

The output of ChatGPT is indeed wrong all over the place. It is similar to the correct answer, but absolutely cannot be used blindly.

Also in this case you don’t need to use OnInternalMouseEnter. It could be used to solve this, but there’s a simpler solution that doesn’t require it in this case.

Assign OnUpdate handler that watches whether mouse is over the button, and changes the color as you wish. Something like

procedure TMyView.MyButtonUpdate(const Sender: TCastleUserInterface;
  const SecondsPassed: Single; var HandleInput: Boolean);
begin
  if MyButton.Focused then
    MyButton.CustomTextColor := SomeColor
  else
    MyButton.CustomTextColor := OtherColor;
end; 

and assign it in view Start like

MyButton.OnUpdate := @MyButtonUpdate;

Yes, that’s why I need your expertise. :wink:

Thanks for the update procedure, it works fine and I have expanded it with “Pressed” conditons after “Focused”. so I can use this procedure for more actions at once. Thanks!

Glad you’re back

Why the event veing called onINTERNALmouseenter rather than regular OnMouseEnter ?

This naming is suspicious

OnUpdate solution requires an extra if which the MouseEnter/MouseLeave solutions do not, so it is not simpler.

Also, i suspect it to be worse on performance:

  1. OnUpdate would probably get called many times more, that actual entering and exiting would happen.
  2. Every call would be slower due to having to check the conditions, which can even be delegated to complex callers
  3. Many redundant calls of .CustomTextColors := same value as before

I see you just want to keep people away from the OnInternalxxxx events, despite those been made exactly for the problem announced (“when I move the cursor over”)

The OnInternalMouseEnter/Leave have Internal in the name because some semantics of them are not precisely defined in some edge-cases, and in fact may change in future implementation.

  • what happens if you destroy the control over which the mouse was?
  • Or move it around – what if you move it suddenly by 1000 pixels or to different parent?
  • Or if you animate UI and move it just slightly to right?
  • What if view is stopped or paused?
  • What if user resizes window by keyboard and causes mouse to no longer be over control this way?
  • Also, are the names really OK? They talk about “mouse” but really work based “pointer focus” in CGE and make sense on mobile too.

In all these cases there’s a question should the engine try aggressively make sure that every OnInternalMouseEnter is matched with OnInternalMouseLeave, or maybe sometimes this guarantee is not necessary. Making this guarantee in some cases would complicate the code significantly.

… And maybe at some point we’ll just have to do this.

But for now, I decided this is not necessary – I didn’t see a convincing argument where these events are desperately needed.

I indeed don’t encourage people to use internal stuff. “Internal” means it can disappear / change meaning / or be renamed to non-internal at any moment. In the ideal future, there must be a way to do everything without using “internal” stuff.

Performance isn’t very relevant here. It’s just an if. And setter with ignore assignment to the same color. This has practically zero effect on execution speed. So don’t worry about performance, write code that is easy to maintain.

same as with any other event, no? is OnUpdate any different with regard to sudden Sender.Destroy ?

Well, good implementation would handle it. And i am not saying that is simple to make - it is not.
There is also one more edge case, Alt+Tab to another application

And if-less code is easier, when everything else the same.
Otherwise we would still make the Windows application ussing nested case in a single mega-loop, like it was all but required in Win16 era.

Granted, if the events are not reliable, then “everything else the same” is not held

By “what happens” I meant here “do we emit OnInternalMouseLeave just before the control is destroyed, and then OnInternalMouseEnter immediately afterwards for the control that was underneath”? That is not clarified at this point, and that’s one reason why these events are internal for now, and discouraged from use.

Naturally destroying the controls in Update will work, but the question is basicallly “how rigorously do we make sure that every possible OnInternalMouseEnter is matched with OnInternalMouseLeave on the same control, and how soon it happens (e.g. can it have 1-frame delay in case of Ui animations)”.