Modify the "arrow" with of a split button in TActionToolBar - delphi

I have a toolbar using TActionToolBar and TActionManager. A button has sub-buttons that are available clicking the small down arrow placed in the right of the button.
The width of the "arrow down" button is very thin and requires precise mouse control. How can I customize it?
Thank you

A solution is using the OnGetControlClass event of TActionToolBar.
Before, it is necessary to derive a class from TThemedDropDownButton and override the GetDropDownButtonWidth function:
function TThemedDropDownButtonEx.GetDropDownButtonWidth: Integer;
begin
Result := 14; // default drop down button width
end;
Then, in OnGetControlClass function:
void __fastcall TWorkAreaToolBarFrame::ActionToolBarLeftGetControlClass(TCustomActionBar *Sender,
TActionClient *AnItem, TCustomActionControlClass &ControlClass)
{
if(ControlClass == __classid(TThemedDropDownButton))
ControlClass = __classid(TThemedDropDownButtonEx);
}
In few words, in GetControlClass event, the toolbar allows you to define which button class you want to use. We use a custom class with the default width changed.

Related

CBuilder TMenu showing in wrong place

I have a Form that needs to be embedded in another Form. I'm placing on TCard:
EmbeddedForm->Parent = ACard;
EmbeddedForm->BorderStyle = bsNone;
EmbeddedForm->Align = alClient;
EmbeddedForm->Show();
All is working well, except that menus on the EmbeddedForm are offset to where they would be if the form were located in top left of the screen. To be clear, the menu shows up in the proper place on the EmbeddedForm, but when clicked, the sub-menu is in the wrong place.
I've tried modifying the DrawItem event, but so far I can't call the base class DrawItem() as it's protected:
void __fastcall TEmbeddedForm::File1DrawItem(TObject *Sender, TCanvas *ACanvas, TRect &ARect, bool Selected)
{
ARect.Left = MainMenu1.Left; // or some other calculation, not important yet
ARect.Top = MainMenu1.Top;
// ??? how to do normal drawItem from here?
}
I'm thinking, either I have to draw it myself (I don't want to) or somehow explain to TMainMenu where it's actually located (preferred solution).

How do I add Components dynamically during runtime in Builder XE6?

I want to add new panels to my form during runtime, but I have the problem that, when aligning them to the top, they are not displayed in the order that I created them.
I followed the hints from this post with the DisableAlign() and EnableAlign()
How to dynamically create controls aligned to the top but after other aligned controls?
This works for the initial four panels I add.
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TPanel * test;
Panel1->DisableAlign();
for(int i = 0; i<4; i++){
test = new TPanel(Panel1);
test->Caption = i;
test->Parent = Panel1;
test->Align = alTop;
}
Panel1->EnableAlign();
}
But then I want to add another panel when clicking the button:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Panel1->DisableAlign();
TPanel * test;
test = new TPanel(Panel1);
test->Caption = 5;
test->Parent = Panel1;
test->Align = alTop;
Panel1->EnableAlign();
}
and this comes up:
Is there any way to get the alignment to do what I want without messing around with the Top Settings or without rebuilding the whole form?
It's quite simple. You must set Top to an appropriate value before setting Align. Set Top to be the coordinate of the bottom of the bottom panel.

TEdit and focus selection works different depending on Show/showmodal

When switching focus between TEdits, the selection changes depending on the way you show the form.
When you show it with Form.show, and swith between two TEdits, the text is selected.
When you show the form with Form.Showmodal, And switch between, the cursor is at the end of the newly focused TEdit
reproduce :
Create a new form with 2 TEdits, type some text in both. Then switch between both TEdits, the whole text is selected, but when I show the form with Modal, The caret is positioned behind the text.
Why is there a difference in functionality? And where can I change it.
I found the code responsible :
procedure TStyledEdit.DoEnter;
var
Form: TCommonCustomForm;
begin
inherited;
Form := TCommonCustomForm(Root);
if not Model.IsReadOnly and Model.InputSupport and not FTextService.HasMarkedText and
((Form = nil)
//next part returns false
or (Form.FormState * [TFmxFormState.Showing] = [TFmxFormState.Showing]) or
(Form.FormState = [TFmxFormState.Engaged])) then
Edit.SelectAll
else
begin
UpdateSelectionPointPositions;
UpdateCaretPosition;
end;
end;
DoEnter is a protected method and as such you can override with your own method if you wish.
You can either do this the classic way by creating your own descendant class (with a different type name) or you can use the so-called interceptor classes as described in this link: interceptor classes.
I believed that you need to extend the if clause like this (but not tested - sorry)
if not Model.IsReadOnly and Model.InputSupport and not FTextService.HasMarkedText and
((Form = nil)
or (Form.FormState * [TFmxFormState.Showing] = [TFmxFormState.Showing])
or (Form.FormState * [TFmxFormState.Modal] = [TFmxFormState.Modal])
or (Form.FormState = [TFmxFormState.Engaged])) then

how to make list with buttons at each item in Delphi

As shown in figure, I need to make a list with buttons on each item, one button at down left of the item, and some on the down right.
I Make the demo app using ListBox control and some Buttons within Panel above on ListBox, but when the ListBox scrolling, it's difficult to make the Buttons follow the ListItem.
who can help, thanks~~~
I got the way to make it!so, I'll post the answer myself~, but Remy Lebeau inspired me.
in the beginning, I used DrawFrameControl() to make button on list, It works but the style was look like classic windows style, and it's hard to make back color like the pic in the example.
then, I used FillRect() and DrawEdge() make the Button, I think it's well, here is the code:
hitPoint := lst1.ScreenToClient(Mouse.CursorPos);
// there is a btnRect var of the Button Rect
edgeRect.Left := btnRect.Left - 1;
edgeRect.Top := btnRect.Top - 1;
edgeRect.Right := btnRect.Right + 1;
edgeRect.Bottom := btnRect.Bottom + 1;
// make button
lst1.Canvas.FillRect(btnRect);
// make edge, FListMouseDown is bool var and setting value at MouseDown/MouseUp Event
//
if PtInRect(edgeRect, hitPoint) and FListMouseDown then begin
DrawEdge(lst1.Canvas.Handle, edgeRect, EDGE_ETCHED, BF_RECT); // button down style
end else begin
DrawEdge(lst1.Canvas.Handle, edgeRect, EDGE_RAISED, BF_RECT);
end;
The following work is store the Rect of Buttons in memory, write the ButtonOnClick event code, and calling ButtonOnClick event at ListMouseUp() event after judge if Mouse Hit Position is in the Button Rect, The Code is not important like the above drawing Buttons, so it is omitted

TStringGrid: Why OnClick after OnKeyDown

Why does a Delphi StringGrid sometimes calls the OnClick event after an OnKeyDown?
Debugging screenshot:
My OnKeyDown event handler:
var
Top: Integer;
Bottom: Integer;
CurrentRow: Integer;
begin
Top := Grid.TopRow;
Bottom := Grid.TopRow + Grid.VisibleRowCount - 1;
if (Key = 38) then CurrentRow := Grid.Row - 1
else if (Key = 40) then CurrentRow := Grid.Row + 1;
// Disable OnClick because sometimes a 'TStringGrid.Click' is called anyway...
// (when clicking on form top window bar and navigating)
Grid.OnClick := nil;
if (CurrentRow < Top - 1) or (CurrentRow > Bottom + 1) then begin
if (Key = 38) then Grid.Row := Bottom
else if (Key = 40) then Grid.Row := Top;
end;
Grid.OnClick := GridClick;
end;
Edit:
It seems the 'OnClick' is not being fired when the last line is selected and pushing 'Down' or when the first line is selected and pushing 'Up'.
Way to reproduce:
Add a TStringGrid to a form and populate with a few lines. Add 'OnClick' and 'OnKeyDown' handler. No specific code needs to be added in these two handler methods. Select a row in the stringgrid on the form and press the up or down arrow on your keyboard.
Edit 2:
This isn't the solution, but to prevent the code in 'OnClick' being executed after pressing up, down, pageup or pagedown, I set a variable in 'OnKeyDown' what key was pressed and check for that variable in 'OnClick'.
Edit 3:
Updated stack trace and way to reproduce.
Well, that hard-coded key codes aren't the case making the least bit transparent, but you witness this effect when you use a direction key (up, down, left, etc...) to change the selection.
Why the OnClick event handler is called, is because TCustomGrid.OnKeyDown calls TCustomGrid.FocusCell, which calls Click.
Exactly why changing focus to another cell would establish a click I do not know, we would have to ask the developers I imagine. Perhaps to simulate the default behaviour when changing focus to another cell by clicking instead of keyboard.
Since you seem to handle direction key presses yourself, maybe you could consider to prevent this from happening at all by ignoring the key any further:
if Key in [VK_PRIOR..VK_DOWN] then
Key := 0;

Resources