Can you give me the names of the functions needed for this purpose? I'm using Delphi XE 5. I want to get this effect:
Window: half transparent
Font: fully visible.
I will use "System" font (zero problems with AA)
What do I look on MSDN? What functions (name) do I need to use?
This is basically the same idea as in Marcus' answer, but with some enhancements. You might have to adjust this to your needs, but the principle is the following:
Create form1 with the following properties:
AlphaBlend := True;
AlphaBlendValue := 128;
BorderStyle := bsNone;
Create form2 with the controls as desired and the following properties:
Color := clFuchsia; // or whatever color is not used
TransparentColor := true;
TransparentColorValue := Color;
Declare a Boolean field in form1 named AllowMove.
In TForm1.FormShow call the following code:
begin
form2.BorderStyle := bsNone;
form2.SetBounds(0, 0, ClientWidth, ClientHeight);
form2.Show;
AllowMove := true;
end;
Declare a Boolean field in form1 named AllowMove and a message handler for WM_MOVE:
procedure WMMOVE(var Message: TMessage); message WM_MOVE;
procedure TForm1.WMMOVE(var Message: TMessage);
begin
inherited;
if AllowMove then begin
Form2.Left := Message.LParamLo;
Form2.Top := Message.LParamHi;
end;
Message.Result := 0;
end;
The only way that I know to get that kind of effect is to render the window contents to an in-memory bitmap, then apply the desired alpha values to the non-font pixels, and then use UpdateLayeredWindow() to display the bitmap on a window. You cannot achieve that affect with a TForm as it relies on SetLayeredWindowAttributes() instead.
Create a 32bit bitmap and draw the desired background on it with alpha values, using a separate array to keep track of the current pixel values in the spots you are going to draw text on, then draw the actual text and use the array to detect which pixels were changed so you can clear the alpha values from just those pixels. Then display the bitmap.
You can get something close by layering two forms over each other. Set the bottom form's color to blue, enable AlphaBlend, and set AlphaBlend to something like 100. That just provides the blue background.
On second form, set TransparentColor to clBtnFace, and put your label there. Set the label font's quality to fqAntialiased.
Set both form's BorderStyle to bsNone.
Lay the second form over the first form, and there you go.
This might be workable if you don't plan on letting the user move the forms, or you move them together.
Related
I want to show numbers in the middle of an Ellipse as text drawn on a canvas. The coordinates will be stored (for the ellipse) inside of a database, as well as the text value will be stored in another part of a database.
What I have done so far is I have been working w/ a demonstration project (DrawApp) from FMXExpress (Github) where I have changed a few procedures from being Private to Public. These procedures include StartDrawing(startP:TPointF), EndDrawing(startP:TPointF), DoDraw() that way I can use these functions from the external Unit/Object. The object uses these functions in coordination with MouseUp/MouseDown, as well as few properties including fDrawing to distinguish whether or not drawing is in progress, and just what tool is being used (fdEllipse).
My Main form uses the following code inside the FormCreate to initially create the fdrawbox := TMyPaintBox.Create(Rectangle1); The Rectangle1 sits on top of an image, which represents a grid to show a body part, and will be able to draw circles on top of the image. What I have found is that it is not hard to create either the text or the ellipse, but for the purpose of creating multiple circles with an identifier to distinguish circles, as I have mentioned, I want to have a number to show up which circle is which. And even in the future, I may want to change the colour to show which circle to concentrate on.
demonstration for mypaintbox http://www.abatepain.com/abate/OHlbF.jpg
So the following code (Delphi FMX) shows creating a drawapp by utilising a TRectangle as its parent.
with fdrawbox do begin
Parent := Rectangle1;
Visible := True;
ForegroundColor := TAlphaColor($FF000000); //
BackgroundColor := TAlphaColor($00000000); //
FuncDraw := TFunctionDraw.fdEllipse; //fdrawbox.fDrawing := True;
StartDrawing(PointF(100, 100));
EndDrawing(PointF(200, 200));
FuncDraw := TFunctionDraw.fdNone;
OnPaint := PaintBox1Paint;
end;
The circle is created using the last few lines, but in order to utilise FillText, I need to use a OnPaint Function, which I created and the code looks something like this. I believe that DrawApp handles OnPaint function internally, but just how it handles it is still unknown. But it is never the less a necessity in order to print "Hello Text!!"
procedure TMainForm.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
with Canvas do begin
BeginScene();
//Clear(cbbg.Color);
Font.Style := [];
Font.Size := 12;
Fill.Color := TAlphaColors.Red;
FillText(TRectF.Create(0, 0, 300, 295), 'Hello Text!!', false, 100, [], TTextAlign.Center, TTextAlign.Center); //TFillTextFlag.RightToLeft
EndScene;
end;
Application.ProcessMessages;
end;
Can someone give an example of how to handle this (possibly inside a single function) where I can print multiple circles and have the associated text follow with it? I believe with the previous example, I could do it on my own, but that I would have to manually enter the PointF (for the circle) and TRectF for the Text, and they both use different values as coordinates.
As you have noted, the TMyPaintBox class doesn't support text rendering, nor properties often used with text output like font or color etc. But you can add those yourself by defining fields in the private section and the properties to get/set the values in the public section.
In the following I assume addition of fields ftextout, ffontsize and ffontcolor with corresponding properties TextOut FontSize and FontColor.
To add functionality for rendering text in the similar way other element types are drawn, add fdText as a new enum to TFunctionDraw.
TFunctionDraw=(fdNone,fdPen,fdLine,fdRectangle,fdEllipse,fdFillBgr,fdBitmapStamp,fdPolyLine, fdText);
Then in TMyPaintBox.DoDraw add a new case option to case ffdraw of like for example:
with vCanvas do
begin
BeginScene();
case ffdraw of
//
// other TFunctionDraw enums
//
TFunctionDraw.fdText: begin
{Canvas.}Font.Size := ffonsize; // new field
{Canvas.}Fill.Color := ffontcolor; // new field
{Canvas.}FillText(r, TextOut, False, 1, [],
TTextAlign.Center, TTextAlign.Center);
end;
end;
Edit:
The references to Canvas in the TFunctionDraw.fdText are superfluous. Remove the outcommented references. The canvas to use is already defined in a with statement (added to the code to show). Oh, I hate those withs!
It is also worth to notice, that if you only want to display circles with text, and not let the user draw on the canvas, you could achieve it much simpler with a component of your own make.
Also, do not call DoDraw directly. It is called by Paint which is fired whenever the fdrawbox is invalidated. So, call invalidate instead when you want to force an update.
End of edit
Then you can achieve text rendering just as any other drawing of elements (using your code template):
with fdrawbox do begin
Parent := Rectangle1;
Visible := True;
ForegroundColor := TAlphaColor($FF000000); //
BackgroundColor := TAlphaColor($00000000); //
FuncDraw := TFunctionDraw.fdEllipse; //fdrawbox.fDrawing := True;
StartDrawing(PointF(100, 100));
EndDrawing(PointF(200, 200));
FuncDraw := TFunctionDraw.fdText;
FontSize := 12; // set new property
FontColor := TAlphaColorRec.Red; // set new property
TextOut := 'Hello text!';
StartDrawing(PointF(100, 100));
EndDrawing(PointF(200, 200));
FuncDraw := TFunctionDraw.fdNone;
invalidate;
// OnPaint := PaintBox1Paint; // no need for this
end;
I have a TDBRadioGroup that I've added to my form.
I'd really like to have the caption to the left of it instead of on top (the form's a little busy and tall, and I'm trying to squeeze it in).
I can add my own label to the left of the Radio Group. But the control insists on reserving space of a Caption that does not exists. Is there a way I can turn it off completely?
The best we've come up with so far is sticking it on a TPanel and then hiding the top couple lines off-panel.
A TGroupBox (and it's descendant TDBGroupBox) are basically wrappers around the Windows GroupBox. The control is designed to sport a user-defined label across the upper-left corner, and doesn't have any style setting to remove it.
So, short of creating your own control to host a series of TRadioButton controls yourself and display them, there's no built-in way to disable the space reserved for the caption. You can suppress the text, of course, by setting the Caption := '', but the padding for the text descenders is not removed simply because the caption isn't displayed.
You can override the paint procedure for TRadioGroup so that the frame is drawn closer to the top of your item list. You could create a new component of type TNoCaptionRadioGroup. You might still have to use the panel trick that you have tried, but by lowering the top of the frame you can grab the space consumed by the non-existent caption. Something like this:
tNoCaptionRadioBox = class(TRadioGroup)
protected
procedure paint; override;
end;
procedure tNoCaptionRadioBox.paint;
var
H: Integer;
R: TRect;
begin
with Canvas do
begin
Font := Self.Font;
H := TextHeight('0');
R := Rect(0, H, Width, Height);
if Ctl3D then
begin
Inc(R.Left);
Inc(R.Top);
Brush.Color := clBtnHighlight;
FrameRect(R);
OffsetRect(R, -1, -1);
Brush.Color := clBtnShadow;
end else
Brush.Color := clWindowFrame;
FrameRect(R);
end;
end;
This is taken from the code for painting a TCustomGroupBox. I have removed the code for drawing the caption and have changed the top of the frame to the full height of the Font. Your actual captioned radio buttons will still be drawn where Windows wants them to be and with the default spacing.
Remember to register the new component by running the package installation tool.
procedure Register;
begin
RegisterComponents('myComponents', [tNoCaptionRadioBox]);
end;
Im trying to change the gradient color of TSpeedbutton at runtime using ApplyStyleLookup, but for some reason only the top twothird of the button changes color. When I change it at design time I see three points for the gradient. I'm using the buttonstyle in the TSpeedbuttons StyleLookup. Using Delphi XE6 Rad Studio.
Thanks.
Added 8/29/14 I found the way to change the gradient see below. But my problem is on the buttonstyle have three rectangles and the one I need to access is the rectangle2 under background. What do I need to access it.
procedure TForm1.SpeedButton1ApplyStyleLookup(Sender: TObject);
var
BckObject: TFmxObject;
begin
BckObject := SpeedButton1.FindStyleResource('background');
if Assigned(BckObject) and (BckObject is TRectAngle) then
begin
TRectAngle(BckObject).Fill.Gradient.Style := TGradientStyle.Linear;
TRectAngle(BckObject).Fill.Gradient.Points.Points[0].Color := $FF0097A5;
TRectAngle(BckObject).Fill.Gradient.Points.Points[0].Offset := 0.25;
TRectAngle(BckObject).Fill.Gradient.Points.Points[1].Color := $FF0097F5;
TRectAngle(BckObject).Fill.Gradient.Points.Points[1].Offset := 1.00;
end;
end;
The rectangle2 (TRectAngle) under background has the StyleName property blank, by enter a name in the StyleName and save it I can access the rectangle2 by using the same method:
BckObject := SpeedButton1.FindStyleResource('myname');
I use Delphi7, PageControl with owner-draw. I can't get so plain and nice look of tabs, as I see on not-owner-drawn PageControls. What's bad:
when using owner-draw, I can't draw on "entire" tab header area, small 1-2px frame around tab header is painted by OS.
1) Delphi not owner-draw, look is OK too (XPMan used):
2) Delphi owner-draw, you see not entire tab header can be colored (XPMan used):
I draw current tab with blue and others with white, here. Only example.
Code:
procedure TForm1.PageControl1DrawTab(Control: TCustomTabControl;
TabIndex: Integer; const Rect: TRect; Active: Boolean);
var
c: TCanvas;
begin
c:= (Control as TPageControl).Canvas;
if Active then
c.Brush.Color:= clBlue
else
c.Brush.Color:= clWhite;
c.FillRect(Rect);
end;
2b) Delphi owner-draw in real app (XPMan used):
Why do i need to use owner-draw? Simple. To draw X button on tab headers, to paint upper-line with custom color, to paint icons from imagelists.
I'm looking for a way to paint ENTIRE rect of tab headers, not decreased rect which is given to PageControl owner-draw events. I tried to increase the rect given by owner-draw events, but this doesn't help, OS repaints this thin 1-2px frame around tab headers anyway.
The tabs of an owner drawn native "tab control" (TPageControl in VCL, although its ascendant is appropriately named TCustomTabControl - it is anyone's guess why the creative naming..), is expected to be painted by its parent control while processing WM_DRAWITEM messages, as documented here.
The VCL takes the burden from the parent by mutating the message to a CN_DRAWITEM message and sending it to the control itself. In this process the VCL has no further intervention. It just calls the OnDrawTab message handler if it is assigned by user code, passing appropriate parameters.
So, it's not the VCL that draws the borders around tabs, but the OS itself. Also, evidently, it doesn't do this during processing of WM_DRAWITEM messages but later in the painting process. You can verify this by putting an empty WM_DRAWITEM handler on the parent of a page control. Result is, whatever we paint in the event handler, it will later get borders by the OS.
What we might try is to try to prevent what the OS draws take effect, we have the device context (as Canvas.Handle) after all. Unfortunately this route also is a dead end because the VCL, after the event handler returns, restores the device context's state.
The only way, then, we have is to completely abandon handling an OnDrawTab event, and acting upon CN_DRAWITEM message. Below sample code use an interposer class, but you can subclass the control any way you like. Make sure that OwnerDrawn is set.
type
TPageControl = class(comctrls.TPageControl)
protected
procedure CNDrawitem(var Message: TWMDrawItem); message CN_DRAWITEM;
end;
TForm1 = class(TForm)
..
..
procedure TPageControl.CNDrawitem(var Message: TWMDrawItem);
var
Color: TColor;
Rect: TRect;
Rgn: HRGN;
begin
Color := 0;
// draw in different colors so we see where we've drawn
case Message.DrawItemStruct.itemID of
0: Color := $D0C0BF;
1: Color := $D0C0DF;
2: Color := $D0C0FF;
end;
SetDCBrushColor(Message.DrawItemStruct.hDC, Color);
// we don't want to get clipped in the passed rectangle
SelectClipRgn(Message.DrawItemStruct.hDC, 0);
// magic numbers corresponding to where the OS draw the borders
Rect := Message.DrawItemStruct.rcItem;
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then begin
Inc(Rect.Left, 2);
// Inc(Rect.Top, 1);
Dec(Rect.Right, 2);
Dec(Rect.Bottom, 3);
end else begin
Dec(Rect.Left, 2);
Dec(Rect.Top, 2);
Inc(Rect.Right, 2);
Inc(Rect.Bottom);
end;
FillRect(Message.DrawItemStruct.hDC, Rect,
GetStockObject(DC_BRUSH));
// just some indication for the active tab
SetROP2(Message.DrawItemStruct.hDC, R2_NOTXORPEN);
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then
Ellipse(Message.DrawItemStruct.hDC, Rect.Left + 4, Rect.Top + 4,
Rect.Left + 12, Rect.Top + 12);
// we want to clip the DC so that the borders to be drawn are out of region
Rgn := CreateRectRgn(0, 0, 0, 0);
SelectClipRgn(Message.DrawItemStruct.hDC, Rgn);
DeleteObject(Rgn);
Message.Result := 1;
inherited;
end;
Here is how the above looks here:
From what I can tell, you are simply looking to have themed painting of your application. In Delphi 7, all you need to do to achieve that is to add an application manifest that specifies the use of comctl32 version 6. The simple way to do so is to add a TXPManifest component to one of your forms or data modules, or just to reference the XPMan unit in your project.
Since you want the system to paint your page control, you must not do any owner drawing.
I have a report that uses a TChart that I am maintaining. One of the TLineSeries that gets added automatically gets assigned the color clWhite, which is too close to the background (clBtnFace).
If I change it, then the next series that gets added takes clWhite. So short of going back and changing it after all the other series are created, is there some way to tell the TChart that I don't want any of my series to be clWhite?
When a series is added to the TChart the TChart assigns it a color. I want it to not assign clWhite.
OK not one to give up easily, I did some more searching. There is a unit variable called ColorPalette of type TColorArray in the TeeProcs unit. If I find and replace white with a different color that fixes it. There may be an instance copy of it. I'll keep looking since that would be preferred.
To revert the ColorPalette back just call the unit method SetDefaultColorPalette in the same unit.
SetDefaultColorPalette; // Make sure we start with the default
ColorPalette[4] := $007FFF; // Change White to Orange
try
// add series to the chart
finally
SetDefaultColorPalette; // Set it back to Default
end;
BTW, I can't accept as answer because I asked the question too, but I tested it and it works.
Near as I can tell from the TeeCharts module; no you can't specify a color that it should not be as it ships.
You can programatically walk through all the TLineSeries entries making sure at run-time that they don't use clWhite.
Say you have an array of acceptable colors clArray, you can use the following code to set the colors of each of the tLineSeries entries at run time.
procedure TForm1.setColors(aChart: TChart; aColorArray: array of TColor);
var
chi : Integer;
coi : Integer;
begin
coi := low(aColorArray);
for chi := 0 to aChart.SeriesList.Count - 1 do begin
aChart.SeriesList[chi].Color := aColorArray[coi];
inc(coi);
if coi > high(aColorArray) then
coi := low(aColorArray);
end;
end;
procedure TForm1.FormShow(Sender: TObject);
var
ca : array of TColor;
begin
setLength(ca, 3);
ca[0] := clRed;
ca[1] := clBlue;
ca[2] := clGreen;
setColors(Chart1, ca);
end;
You can use the series methods ClearPalette then AddPalette to create your custom palette.