In Lazarus how do I find the "real" font values used on Form1? - lazarus

Inside Lazarus the default font values for Form1 are:
Form1.Font.Name=default
Form1.Font.Size=0
How do I find what the actual "real" font name and font size is for these default values?

This code seems to work:
procedure TForm1.GetFormFontName;
var
S : String;
begin
S := GetFontData(Self.Font.Handle).Name;
Caption := S;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
GetFormFontName;
end;
GetFontData returns a TFontData record
TFontData = record
Handle: HFont;
Height: Integer;
Pitch: TFontPitch;
Style: TFontStylesBase;
CharSet: TFontCharSet;
Quality: TFontQuality;
Name: TFontDataName;
Orientation: Integer;
end;
This does not include the font's Size, which is an explicit published property of the font.
The code above is derived from this thread: https://forum.lazarus.freepascal.org/index.php?topic=16697.0, which I found as the first hit returned by this google query
font name default site:freepascal.org

Related

Is it possible to made a TCombo edit caret 'wider' or to 'bold' it?

I have an mode that uses TComboBox.SelStart to indicate progress along the edit text string. In this mode I would like to make some kind of change to the edit caret, for example to widen it to 2 pixels or 'bold' it in some way to indicate this mode and to have it grab more attention. Is this possible?
Yes, as Alex mentioned in his comment, this can be done using API calls. Example:
procedure SetComboCaretWidth(ComboBox: TComboBox; Multiplier: Integer);
var
EditWnd: HWND;
EditRect: TRect;
begin
ComboBox.SetFocus;
ComboBox.SelStart := -1;
Assert(ComboBox.Style = csDropDown);
EditWnd := GetWindow(ComboBox.Handle, GW_CHILD);
SendMessage(EditWnd, EM_GETRECT, 0, LPARAM(#EditRect));
CreateCaret(EditWnd, 0,
GetSystemMetrics(SM_CXBORDER) * Multiplier, EditRect.Height);
ShowCaret(EditWnd);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SetComboCaretWidth(ComboBox1, 4); // bold caret
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
SetComboCaretWidth(ComboBox1, 1); // default caret
end;

Looking for a custom image grid

I'm trying to find a grid which is especially designed to show images. It needs to have good performance too, and preferably with some sort of thumbnail cache. The images need to be loaded from files, and it would be good if images can be assigned dynamically too. It shouldn't work on a list of col/row records like standard grids, but a single list of items, each item representing an image. There should be a property to define col width and row height for all cols and rows at once, not one at a time. The end goal is to list all images with user options to control how large to display the images. It will be used as a product display, so there needs to be some sort of custom drawing capability too, like an OnDrawItem event. This may display up to 50,000 images in this list, so TListView won't work, as it's very heavy for this.
It needs to work with Delphi 2010, XE2, and preferably 7 too.
Here's 3 examples of how to display 8 images below. I don't mean each image being a different size, but exactly the same size. No 2 columns can have different widths, and same with rows.
For the fun of it, I wrote an ImageGrid component for you.
It has only a vertical scroll bar; resizing the width of the control adjusts the column count and row count. The images are cached as resized bitmaps in an internal list, along with their file names.
Because loading and resampling these images may take some time, depending on image count, resolution and whether you want to use the Graphics32 library for better resample quality, the component delegates the loading process to a separate thread, which (re)runs on setting the column width or the row height, and on changing the file names or the folder path in which the component tries to find all images of types to be supplied in the FileFormats property.
Features:
Creates and resizes image thumbs in a background thread, from file names with the GDI+ library or from manually added images with the Graphics 32 library
Automatically recognizes all registered image file formats
Animated scrolling
Touchscreen support for scrolling by dragging the grid
Keyboard support for selecting thumbs
OwnerDraw support, e.g. for adding captions to the thumbs
Virtual support for bypassing the automatic creation of thumbs
Properties and events:
ColCount: number of columns, readonly
Count: number of images, readonly
Images: list of all manually added images where the thumbs are internally created from
Items: list of all filename-thumbnail or image-thumbnail combinations
RowCount: number of rows, readonly
Thumbs: list of all internally created thumbs
AutoHideScrollBar: hides the scroll bar when all rows are visible
BorderStyle: shows or hides themed border
BorderWidth: margin of the component, outside of the scroll bar
CellAlignment: alignes thumbs at the left, center or right of the cell
CellHeight: height of cell
CellLayout: alignes thumbs at the top, middle or bottom of the cell
CellSpacing: spacing between the cells
CellWidth: width of cell
Color: background color of border and cell spacing
ColWidth: width of column (equals width of cell plus cell spacing)
DefaultDrawing: draws all thumbs by default
DesignPreview: shows thumbs in the designer
DragScroll: supports scrolling the grid by draging the grid
FileFormats: image file name extensions by which the file names are filtered
FileNames: list holding all file names
Folder: the directory in which the component tries to find all images files
ItemIndex: selected cell index
MarkerColor: color of temporarily thumb marker during loading process
MarkerStyle: style of temporarily thumb marker during loading process
OnClickCell: fires when a cell is clicked
OnDrawCell: fires when a cell is drawn
OnMeasureThumb: fires when the size of a thumb is to be calculated
OnProgress: fires when an image is resized to thumb format
OnUnresolved: fires when a thumb cannot be created, e.g. when file name is not found
RetainUnresolvedItems: keeps empty thumbs in the list
RowHeight: the row height (equals cell height plus cell spacing)
ParentBackground: draws the (themed) background of the parent in the border and between the cells
Proportional: resizes images proportionally
Sorted: file names are sorted
Stretch: stretches small images up to the cell size
VirtualMode: prevents of automatically creating the thumbs
WheelScrollLines: number of rows to be scrolled with mouse wheel
With thanks to:
InterfaceLIFT for the images shown in the screenshot above
How to create a slowing scroll effect on a scrollbox?
How to get all of the supported file formats from Graphics unit?
Is it necessary to convert String to WideString?
Bugfix for BorderWidth > 0 in combination with a scroll bar?
The code is too long to post here, but the OpenSource project is downloadable from the GitHub server here. This is the interface section:
unit AwImageGrid;
interface
{$DEFINE USE_GR32}
uses
Windows, Classes, SysUtils, Messages, Controls, Graphics, Forms, StdCtrls,
Grids, GDIPAPI, GDIPOBJ, RTLConsts, Math, Themes
{$IFDEF USE_GR32}, GR32, GR32_Resamplers {$ENDIF};
const
DefCellSpacing = 5;
DefCellWidth = 96;
DefCellHeight = 60;
DefColWidth = DefCellWidth + DefCellSpacing;
DefRowHeight = DefCellHeight + DefCellSpacing;
MinThumbSize = 4;
MinCellSize = 8;
type
PImageGridItem = ^TImageGridItem;
TImageGridItem = record
FFileName: TFileName;
FObject: TObject;
FImage: TGraphic;
FThumb: TBitmap;
end;
PImageGridItemList = ^TImageGridItemList;
TImageGridItemList = array[0..MaxListSize div 2] of TImageGridItem;
{ TImageGridItems
The managing object for holding filename-thumbnail or image-thumbnail
combinations in an array of TImageGridItem elements. When an item's image
changes, the item's thumb is freed. When an item's filename changes, then
the item's thumb is freed only if the item's image is unassigned. }
TImageGridItems = class(TStrings)
private
FCapacity: Integer;
FChanged: Boolean;
FCount: Integer;
FList: PImageGridItemList;
FOnChanged: TNotifyEvent;
FOnChanging: TNotifyEvent;
FOwnsObjects: Boolean;
FSorted: Boolean;
procedure ExchangeItems(Index1, Index2: Integer);
function GetImage(Index: Integer): TGraphic;
function GetThumb(Index: Integer): TBitmap;
procedure Grow;
procedure InsertItem(Index: Integer; const S: String; AObject: TObject;
AImage: TGraphic; AThumb: TBitmap);
procedure PutImage(Index: Integer; AImage: TGraphic);
procedure PutThumb(Index: Integer; AThumb: TBitmap);
procedure QuickSort(L, R: Integer);
procedure SetSorted(Value: Boolean);
protected
function CompareStrings(const S1, S2: String): Integer; override;
procedure Changed; virtual;
procedure Changing; virtual;
function Get(Index: Integer): String; override;
function GetCapacity: Integer; override;
function GetCount: Integer; override;
function GetObject(Index: Integer): TObject; override;
procedure Put(Index: Integer; const S: String); override;
procedure PutObject(Index: Integer; AObject: TObject); override;
procedure PutThumbSilently(Index: Integer; AThumb: TBitmap); virtual;
procedure SetCapacity(Value: Integer); override;
procedure SetUpdateState(Updating: Boolean); override;
public
function Add(const S: String): Integer; override;
function AddImage(const S: String; AImage: TGraphic): Integer; virtual;
function AddItem(const S: String; AObject: TObject; AImage: TGraphic;
AThumb: TBitmap): Integer; virtual;
function AddObject(const S: String; AObject: TObject): Integer; override;
function AddThumb(const S: String; AThumb: TBitmap): Integer; virtual;
procedure AddStrings(Strings: TStrings); override;
procedure Assign(Source: TPersistent); override;
procedure Clear; override;
procedure ClearThumbs; virtual;
procedure Delete(Index: Integer); override;
destructor Destroy; override;
procedure Exchange(Index1, Index2: Integer); override;
function IndexOf(const S: String): Integer; override;
procedure Insert(Index: Integer; const S: String); override;
procedure InsertObject(Index: Integer; const S: String;
AObject: TObject); override;
function Find(const S: String; var Index: Integer): Boolean;
procedure Sort; virtual;
property FileNames[Index: Integer]: String read Get write Put;
property Images[Index: Integer]: TGraphic read GetImage write PutImage;
property OnChanged: TNotifyEvent read FOnChanged write FOnChanged;
property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
property Sorted: Boolean read FSorted write SetSorted;
property Thumbs[Index: Integer]: TBitmap read GetThumb write PutThumb;
end;
{ TBorderControl
A control with a system drawn border following the current theme, and an
additional margin as implemented by TWinControl.BorderWidth. }
TBorderControl = class(TCustomControl)
private
FBorderStyle: TBorderStyle;
procedure SetBorderStyle(Value: TBorderStyle);
procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
procedure CMCtl3DChanged(var Message: TMessage); message CM_CTL3DCHANGED;
protected
procedure CreateParams(var Params: TCreateParams); override;
function TotalBorderWidth: Integer; virtual;
public
constructor Create(AOwner: TComponent); override;
property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle
default bsSingle;
property BorderWidth;
end;
{ TAnimRowScroller
A scroll box with a vertical scroll bar and vertically stacked items with a
fixed row height. Scrolling with the scroll bar is animated alike Windows'
own default list box control. Scrolling is also possible by dragging the
content with the left mouse button. }
TAnimRowScroller = class(TBorderControl)
private
FAutoHideScrollBar: Boolean;
FDragScroll: Boolean;
FDragScrolling: Boolean;
FDragSpeed: Single;
FDragStartPos: Integer;
FPrevScrollPos: Integer;
FPrevTick: Cardinal;
FRow: Integer;
FRowCount: Integer;
FRowHeight: Integer;
FScrollingPos: Integer;
FScrollPos: Integer;
FWheelScrollLines: Integer;
procedure Drag;
function IsWheelScrollLinesStored: Boolean;
procedure Scroll;
procedure SetAutoHideScrollBar(Value: Boolean);
procedure SetRow(Value: Integer);
procedure SetRowCount(Value: Integer);
procedure SetScrollPos(Value: Integer; Animate, Snap: Boolean);
procedure UpdateScrollBar;
procedure WMVScroll(var Message: TWMVScroll); message WM_VSCROLL;
protected
procedure CreateWnd; override;
function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer;
MousePos: TPoint): Boolean; override;
procedure DrawFocusRect; virtual;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer); override;
procedure Resize; override;
procedure SetRowHeight(Value: Integer); virtual;
procedure WndProc(var Message: TMessage); override;
property AutoHideScrollBar: Boolean read FAutoHideScrollBar
write SetAutoHideScrollBar default True;
property Row: Integer read FRow write SetRow default -1;
property RowCount: Integer read FRowCount write SetRowCount;
property RowHeight: Integer read FRowHeight write SetRowHeight
default DefRowHeight;
property DragScroll: Boolean read FDragScroll write FDragScroll
default True;
property DragScrolling: Boolean read FDragScrolling;
property ScrollingPos: Integer read FScrollingPos;
property WheelScrollLines: Integer read FWheelScrollLines
write FWheelScrollLines stored IsWheelScrollLinesStored;
public
constructor Create(AOwner: TComponent); override;
procedure MouseWheelHandler(var Message: TMessage); override;
function Scrolling: Boolean;
end;
{ TCustomImageGrid
The base class of an image grid. It shows images from left to right, then
from top to bottom. The number of columns is determined by the width of the
control, possibly resulting in a vertical scroll bar. The coord size is set
by ColWidth and RowHeight, being the sum of CellWidth resp. CellHeight plus
CellSpacing. Each cell shows a thumb of the corresponding image. The control
automatically starts a thumbs generating background thread when an image's
graphic, filename or its cell size is changed. Before every such change, any
previously created thread is terminated. Combine multiple changes by calling
Items.BeginUpdate/Items.EndUpdate to prevent the thread from being recreated
repeatedly. }
TCustomImageGrid = class;
TPath = type String;
TDrawCellEvent = procedure(Sender: TCustomImageGrid; Index, ACol,
ARow: Integer; R: TRect) of object;
TImageEvent = procedure(Sender: TCustomImageGrid; Index: Integer) of object;
TMeasureThumbEvent = procedure(Sender: TCustomImageGrid; Index: Integer;
var AThumbWidth, AThumbHeight: Integer) of object;
TCustomImageGrid = class(TAnimRowScroller)
private
FCellAlignment: TAlignment;
FCellLayout: TTextLayout;
FCellSpacing: Integer;
FColCount: Integer;
FColWidth: Integer;
FDefaultDrawing: Boolean;
FDesignPreview: Boolean;
FFileFormats: TStrings;
FFolder: TPath;
FItemIndex: Integer;
FItems: TImageGridItems;
FMarkerColor: TColor;
FMarkerStyle: TPenStyle;
FOnClickCell: TImageEvent;
FOnDrawCell: TDrawCellEvent;
FOnMeasureThumb: TMeasureThumbEvent;
FOnProgress: TImageEvent;
FOnUnresolved: TImageEvent;
FProportional: Boolean;
FRetainUnresolvedItems: Boolean;
FStretch: Boolean;
FThumbsGenerator: TThread;
FVirtualMode: Boolean;
procedure DeleteUnresolvedItems;
procedure FileFormatsChanged(Sender: TObject);
function GetCellHeight: Integer;
function GetCellWidth: Integer;
function GetCount: Integer;
function GetFileNames: TStrings;
function GetImage(Index: Integer): TGraphic;
function GetRowCount: Integer;
function GetSorted: Boolean;
function GetThumb(Index: Integer): TBitmap;
function IsFileNamesStored: Boolean;
procedure ItemsChanged(Sender: TObject);
procedure ItemsChanging(Sender: TObject);
procedure Rearrange;
procedure SetCellAlignment(Value: TAlignment);
procedure SetCellHeight(Value: Integer);
procedure SetCellLayout(Value: TTextLayout);
procedure SetCellSpacing(Value: Integer);
procedure SetCellWidth(Value: Integer);
procedure SetColWidth(Value: Integer);
procedure SetDefaultDrawing(Value: Boolean);
procedure SetDesignPreview(Value: Boolean);
procedure SetFileFormats(Value: TStrings);
procedure SetFileNames(Value: TStrings);
procedure SetFolder(Value: TPath);
procedure SetImage(Index: Integer; Value: TGraphic);
procedure SetItemIndex(Value: Integer);
procedure SetItems(Value: TImageGridItems);
procedure SetMarkerColor(Value: TColor);
procedure SetMarkerStyle(Value: TPenStyle);
procedure SetProportional(Value: Boolean);
procedure SetRetainUnresolvedItems(Value: Boolean);
procedure SetSorted(Value: Boolean);
procedure SetStretch(Value: Boolean);
procedure SetThumb(Index: Integer; Value: TBitmap);
procedure SetVirtualMode(Value: Boolean);
procedure TerminateThumbsGenerator;
procedure ThumbsUpdated(Sender: TObject);
procedure UpdateThumbs;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
procedure CMExit(var Message: TCMExit); message CM_EXIT;
protected
procedure ChangeScale(M, D: Integer); override;
procedure DoClickCell(Index: Integer); virtual;
procedure DoDrawCell(Index, ACol, ARow: Integer; R: TRect); virtual;
procedure DoMeasureThumb(Index: Integer; var AThumbWidth,
AThumbHeight: Integer); virtual;
procedure DoProgress(Index: Integer); virtual;
procedure DrawFocusRect; override;
procedure InvalidateItem(Index: Integer); virtual;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure Loaded; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer); override;
procedure Paint; override;
procedure Resize; override;
procedure SetRowHeight(Value: Integer); override;
property CellAlignment: TAlignment read FCellAlignment
write SetCellAlignment default taCenter;
property CellHeight: Integer read GetCellHeight write SetCellHeight
default DefCellHeight;
property CellLayout: TTextLayout read FCellLayout write SetCellLayout
default tlCenter;
property CellSpacing: Integer read FCellSpacing write SetCellSpacing
default DefCellSpacing;
property CellWidth: Integer read GetCellWidth write SetCellWidth
default DefCellWidth;
property ColCount: Integer read FColCount;
property ColWidth: Integer read FColWidth write SetColWidth
default DefColWidth;
property Count: Integer read GetCount;
property DefaultDrawing: Boolean read FDefaultDrawing
write SetDefaultDrawing default True;
property DesignPreview: Boolean read FDesignPreview write SetDesignPreview
default False;
property FileFormats: TStrings read FFileFormats write SetFileFormats;
property FileNames: TStrings read GetFileNames write SetFileNames
stored IsFileNamesStored;
property Folder: TPath read FFolder write SetFolder;
property Images[Index: Integer]: TGraphic read GetImage write SetImage;
property ItemIndex: Integer read FItemIndex write SetItemIndex default -1;
property Items: TImageGridItems read FItems write SetItems;
property MarkerColor: TColor read FMarkerColor write SetMarkerColor
default clGray;
property MarkerStyle: TPenStyle read FMarkerStyle write SetMarkerStyle
default psDash;
property OnClickCell: TImageEvent read FOnClickCell write FOnClickCell;
property OnDrawCell: TDrawCellEvent read FOnDrawCell write FOnDrawCell;
property OnMeasureThumb: TMeasureThumbEvent read FOnMeasureThumb
write FOnMeasureThumb;
property OnProgress: TImageEvent read FOnProgress write FOnProgress;
property OnUnresolved: TImageEvent read FOnUnresolved write FOnUnresolved;
property Proportional: Boolean read FProportional write SetProportional
default True;
property RetainUnresolvedItems: Boolean read FRetainUnresolvedItems
write SetRetainUnresolvedItems default False;
property RowCount: Integer read GetRowCount;
property Sorted: Boolean read GetSorted write SetSorted default False;
property Stretch: Boolean read FStretch write SetStretch default False;
property Thumbs[Index: Integer]: TBitmap read GetThumb write SetThumb;
property VirtualMode: Boolean read FVirtualMode write SetVirtualMode
default False;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function CellRect(Index: Integer): TRect;
function CoordFromIndex(Index: Integer): TGridCoord;
procedure Clear; virtual;
function MouseToIndex(X, Y: Integer): Integer;
procedure ScrollInView(Index: Integer);
procedure SetCellSize(ACellWidth, ACellHeight: Integer);
procedure SetCoordSize(AColWidth, ARowHeight: Integer);
property ParentBackground default False;
public
property TabStop default True;
end;
TAwImageGrid = class(TCustomImageGrid)
public
property ColCount;
property Count;
property Images;
property Items;
property RowCount;
property Thumbs;
published
property Align;
property Anchors;
property AutoHideScrollBar;
property BorderStyle;
property BorderWidth;
property CellAlignment;
property CellHeight;
property CellLayout;
property CellSpacing;
property CellWidth;
property ClientHeight;
property ClientWidth;
property Color;
property ColWidth;
property Constraints;
property Ctl3D;
property DefaultDrawing;
property DesignPreview;
property DragCursor;
property DragKind;
property DragMode;
property DragScroll;
property Enabled;
property FileFormats;
property FileNames;
property Folder;
property ItemIndex;
property MarkerColor;
property MarkerStyle;
property OnCanResize;
property OnClick;
property OnClickCell;
property OnConstrainedResize;
property OnContextPopup;
property OnDblClick;
property OnDockDrop;
property OnDockOver;
property OnDragDrop;
property OnDragOver;
property OnDrawCell;
property OnEndDock;
property OnEndDrag;
property OnEnter;
property OnExit;
property OnGetSiteInfo;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnMeasureThumb;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnMouseWheel;
property OnMouseWheelDown;
property OnMouseWheelUp;
property OnProgress;
property OnResize;
property OnStartDock;
property OnStartDrag;
property OnUnDock;
property OnUnresolved;
property ParentBackground;
property RetainUnresolvedItems;
property RowHeight;
property ParentColor;
property ParentCtl3D;
property ParentShowHint;
property PopupMenu;
property Proportional;
property ShowHint;
property Sorted;
property Stretch;
property TabOrder;
property TabStop;
property VirtualMode;
property Visible;
property WheelScrollLines;
end;
I'm using the multi-image view from the ImageEn library. It does everything you asked for, and it's very fast. I'm happy with it. You can still get an older, free version from Torry that works with Delphi 7 (I haven't tried it on XE2).
The methods aren't exactly intuitive, but once you get the hang of it (nice help file included), it works great.
The latest version has some more features, and it's not expensive if you do decide to license it.
You can see a video of ImageEn multi-image view in action in my application.
They even have a Flow View control that looks like the slide show on a Mac.

How do I create a window without any frame regardless of user settings?

I need to write an application that displays two different pictures in two instances of the application. These pictures must look as if they were put side by side on the canvas of the same window but for internal reasons it must be two different applications not a single one. Is there any way to turn off the window frame regardless of what the user's Windows settings are? I still want to retain the title bar and the close/minimize/maximize buttons.
Bonus points if the two (or multiple) windows look and react like a single one to the user.
A Delphi example would be nice but I can probably do with a hint on which flags or whatever to set using Win32 API (no dotNET please).
Since windows with title bars always have borders, your next option is to make a borderless window and then paint a title bar at the top of the window yourself. That means handling mouse messages, too. Start with wm_NCHitTest. To make a borderless window, override your form's CreateParams method and set the Style field so there's no border.
This creates a Form without side or bottom borders:
type
TForm1 = class(TForm)
private
FBorderWidth: Integer;
FTitleHeight: Integer;
procedure AppRestored(Sender: TObject);
procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
procedure WMNCPaint(var Message: TWMNCPaint); message WM_NCPAINT;
protected
procedure Resize; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.AppRestored(Sender: TObject);
begin
Repaint;
end;
procedure TForm1.Resize;
begin
inherited Resize;
if FBorderWidth = 0 then
begin
FBorderWidth := (Width - ClientWidth) div 2;
FTitleHeight := Height - ClientHeight - FBorderWidth;
Application.OnRestore := AppRestored;
end;
Invalidate;
end;
procedure TForm1.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
inherited;
with Message.CalcSize_Params^ do
begin
Dec(rgrc[0].Left, FBorderWidth);
Inc(rgrc[0].Right, FBorderWidth);
Inc(rgrc[0].Bottom, FBorderWidth);
end;
end;
procedure TForm1.WMNCPaint(var Message: TWMNCPaint);
begin
DeleteObject(Message.RGN);
Message.RGN := CreateRectRgn(Left, Top, Left + Width, Top + FTitleHeight);
inherited;
end;

How do I load icons from a resource without suffering from aliasing?

I have a GUI application which includes a number of icons used for toolbar buttons, menu glyphs, notification icons etc. These icons are linked to the application as resources and a variety of different sizes are available. Typically, for toolbar button images I have available 16px, 24px and 32px versions. My icons are 32bpp with partial transparency.
The application is high DPI aware and adjusts the size of all visual elements according to the prevailing font scaling. So, for example, at 100% font scaling, 96dpi, the toolbar icon size is 16px. At 125% scaling, 120dpi, the toolbar icon size is 20px. I need to be able to load an icon of size 20px without any aliasing effects. How can I do this? Note that I would like to support Windows 2000 and later.
On Vista and up a number of new functions were added that make this task trivial. The function that is most appropriate here is LoadIconWithScaleDown.
This function will first search the icon file for an icon having exactly the same size. If a match is not found, then unless both cx and cy match one of the standard icon sizes—16, 32, 48, or 256 pixels— the next largest icon is selected and then scaled down to the desired size. For example, if an icon with an x dimension of 40 pixels is requested by the callign application, the 48-pixel icon is used and scaled down to 40 pixels. In contrast, the LoadImage function selects the 32-pixel icon and scales it up to 40 pixels.
If the function is unable to locate a larger icon, it defaults to the standard behavior of finding the next smallest icon and scaling it up to the desired size.
In my experience this function does an excellent job of scaling and the results show no signs of aliasing.
For earlier versions of Windows there is, to the very best of my knowledge, no single function that can perform this task adequately. The results obtained from LoadImage are of very poor quality. Instead the best approach I have found is as follows:
Examine the available images in the resource to find the image with the largest size that is less than desired icon size.
Create a new icon of the desired size and initialise it to be fully transparent.
Place the smaller icon from the resource in the centre of the new (larger) icon.
This means that there will be a small transparent border around the icon, but typically this is small enough to be insignificant. The ideal option would be to use code that could scale down just as LoadIconWithScaleDown does, but that is non-trivial to write.
So, without further ado here is the code I use.
unit uLoadIconResource;
interface
uses
SysUtils, Math, Classes, Windows, Graphics, CommCtrl;
function LoadIconResourceSize(const ResourceName: string; IconSize: Integer): HICON;//will not throw an exception
function LoadIconResourceMetric(const ResourceName: string; IconMetric: Integer): HICON;
implementation
function IconSizeFromMetric(IconMetric: Integer): Integer;
begin
case IconMetric of
ICON_SMALL:
Result := GetSystemMetrics(SM_CXSMICON);
ICON_BIG:
Result := GetSystemMetrics(SM_CXICON);
else
raise EAssertionFailed.Create('Invalid IconMetric');
end;
end;
procedure GetDIBheaderAndBits(bmp: HBITMAP; out bih: BITMAPINFOHEADER; out bits: Pointer);
var
pbih: ^BITMAPINFOHEADER;
bihSize, bitsSize: DWORD;
begin
bits := nil;
GetDIBSizes(bmp, bihSize, bitsSize);
pbih := AllocMem(bihSize);
Try
bits := AllocMem(bitsSize);
GetDIB(bmp, 0, pbih^, bits^);
if pbih.biSize<SizeOf(bih) then begin
FreeMem(bits);
bits := nil;
exit;
end;
bih := pbih^;
Finally
FreeMem(pbih);
End;
end;
function CreateIconFromSmallerIcon(IconSize: Integer; SmallerIcon: HICON): HICON;
procedure InitialiseBitmapInfoHeader(var bih: BITMAPINFOHEADER);
begin
bih.biSize := SizeOf(BITMAPINFOHEADER);
bih.biWidth := IconSize;
bih.biHeight := 2*IconSize;//height of xor bitmap plus height of and bitmap
bih.biPlanes := 1;
bih.biBitCount := 32;
bih.biCompression := BI_RGB;
end;
procedure CreateXORbitmap(const sbih, dbih: BITMAPINFOHEADER; sptr, dptr: PDWORD);
var
line, xOffset, yOffset: Integer;
begin
xOffset := (IconSize-sbih.biWidth) div 2;
yOffset := (IconSize-sbih.biHeight) div 2;
inc(dptr, xOffset + IconSize*yOffset);
for line := 0 to sbih.biHeight-1 do begin
Move(sptr^, dptr^, sbih.biWidth*SizeOf(DWORD));
inc(dptr, IconSize);//relies on the fact that no padding is needed for RGBA scanlines
inc(sptr, sbih.biWidth);//likewise
end;
end;
var
SmallerIconInfo: TIconInfo;
sBits, xorBits: PDWORD;
xorScanSize, andScanSize: Integer;
xorBitsSize, andBitsSize: Integer;
sbih: BITMAPINFOHEADER;
dbih: ^BITMAPINFOHEADER;
resbitsSize: DWORD;
resbits: Pointer;
begin
Result := 0;
Try
if not GetIconInfo(SmallerIcon, SmallerIconInfo) then begin
exit;
end;
Try
GetDIBheaderAndBits(SmallerIconInfo.hbmColor, sbih, Pointer(sBits));
if Assigned(sBits) then begin
Try
if (sbih.biWidth>IconSize) or (sbih.biHeight>IconSize) or (sbih.biPlanes<>1) or (sbih.biBitCount<>32) then begin
exit;
end;
xorScanSize := BytesPerScanline(IconSize, 32, 32);
Assert(xorScanSize=SizeOf(DWORD)*IconSize);
andScanSize := BytesPerScanline(IconSize, 1, 32);
xorBitsSize := IconSize*xorScanSize;
andBitsSize := IconSize*andScanSize;
resbitsSize := SizeOf(BITMAPINFOHEADER) + xorBitsSize + andBitsSize;
resbits := AllocMem(resbitsSize);//AllocMem zeroises the memory
Try
dbih := resbits;
InitialiseBitmapInfoHeader(dbih^);
xorBits := resbits;
inc(PByte(xorBits), SizeOf(BITMAPINFOHEADER));
CreateXORbitmap(sbih, dbih^, sBits, xorBits);
//don't need to fill in the mask bitmap when using RGBA
Result := CreateIconFromResourceEx(resbits, resbitsSize, True, $00030000, IconSize, IconSize, LR_DEFAULTCOLOR);
Finally
FreeMem(resbits);
End;
Finally
FreeMem(sBits);
End;
end;
Finally
if SmallerIconInfo.hbmMask<>0 then begin
DeleteObject(SmallerIconInfo.hbmMask);
end;
if SmallerIconInfo.hbmColor<>0 then begin
DeleteObject(SmallerIconInfo.hbmColor);
end;
End;
Finally
DestroyIcon(SmallerIcon);
End;
end;
function LoadIconResourceSize(const ResourceName: string; IconSize: Integer): HICON;//will not throw an exception
function LoadImage(IconSize: Integer): HICON;
begin
Result := Windows.LoadImage(HInstance, PChar(ResourceName), IMAGE_ICON, IconSize, IconSize, LR_DEFAULTCOLOR);
end;
type
TGrpIconDir = packed record
idReserved: Word;
idType: Word;
idCount: Word;
end;
TGrpIconDirEntry = packed record
bWidth: Byte;
bHeight: Byte;
bColorCount: Byte;
bReserved: Byte;
wPlanes: Word;
wBitCount: Word;
dwBytesInRes: DWORD;
wID: WORD;
end;
var
i, BestAvailableIconSize, ThisSize: Integer;
ResourceNameWide: WideString;
Stream: TResourceStream;
IconDir: TGrpIconDir;
IconDirEntry: TGrpIconDirEntry;
begin
//LoadIconWithScaleDown does high quality scaling and so we simply use it if it's available
ResourceNameWide := ResourceName;
if Succeeded(LoadIconWithScaleDown(HInstance, PWideChar(ResourceNameWide), IconSize, IconSize, Result)) then begin
exit;
end;
//XP: find the closest sized smaller icon and draw without stretching onto the centre of a canvas of the right size
Try
Stream := TResourceStream.Create(HInstance, ResourceName, RT_GROUP_ICON);
Try
Stream.Read(IconDir, SizeOf(IconDir));
Assert(IconDir.idCount>0);
BestAvailableIconSize := high(BestAvailableIconSize);
for i := 0 to IconDir.idCount-1 do begin
Stream.Read(IconDirEntry, SizeOf(IconDirEntry));
Assert(IconDirEntry.bWidth=IconDirEntry.bHeight);
ThisSize := IconDirEntry.bHeight;
if ThisSize=0 then begin//indicates a 256px icon
continue;
end;
if ThisSize=IconSize then begin
//a perfect match, no need to continue
Result := LoadImage(IconSize);
exit;
end else if ThisSize<IconSize then begin
//we're looking for the closest sized smaller icon
if BestAvailableIconSize<IconSize then begin
//we've already found one smaller
BestAvailableIconSize := Max(ThisSize, BestAvailableIconSize);
end else begin
//this is the first one that is smaller
BestAvailableIconSize := ThisSize;
end;
end;
end;
if BestAvailableIconSize<IconSize then begin
Result := CreateIconFromSmallerIcon(IconSize, LoadImage(BestAvailableIconSize));
if Result<>0 then begin
exit;
end;
end;
Finally
FreeAndNil(Stream);
End;
Except
;//swallow because this routine is contracted not to throw exceptions
End;
//final fallback: make do without
Result := 0;
end;
function LoadIconResourceMetric(const ResourceName: string; IconMetric: Integer): HICON;
begin
Result := LoadIconResourceSize(ResourceName, IconSizeFromMetric(IconMetric));
end;
end.
Using these function is quite obvious. They assume that the resource is located in the same module as the code. The code could readily be generalised to receive an HMODULE in case you needed support for that level of generality.
Call LoadIconResourceMetric if you wish to load icons of size equal to the system small icon or system large icon. The IconMetric parameter should be either ICON_SMALL or ICON_BIG. For toolbars, menus and notification icons, ICON_SMALL should be used.
If you wish to specify the icon size in absolute terms use LoadIconResourceSize.
These functions return an HICON. You can of course assign this to the Handle property of a TIcon instance. More likely you will wish to add to an image list. The easiest way to do this is to call ImageList_AddIcon passing the Handle of the TImageList instance.
Note 1: Older versions of Delphi do not have LoadIconWithScaleDown defined in CommCtrl. For such Delphi versions you need to call GetProcAddress to load it. Note that this is a Unicode only API and so you must send it a PWideChar for the resource name. Like this: LoadIconWithScaleDown(..., PWideChar(WideString(ResourceName)),...).
Note 2: The definition of LoadIconWithScaleDown is flawed. If you call it after the common controls library has been initialised then you will have no problems. However, if you call the function early on in the life of your process then LoadIconWithScaleDown can fail. I have just submitted QC#101000 to report this problem. Again, if you are afflicted by this then you have to call GetProcAddress yourself.

Use HWND (or something similar) as Node Image in Virtual Stringtree

Is it possible to display an Icon obtained from an external Handle, as the Image of my Node in Virtual Stringtree? The Node's Data contains the HWND.
I would use ImageList assigned to your VT's Images property and OnGetImageIndex event.
Here's how to fill the image list using WM_GETICON.
procedure TForm1.Button1Click(Sender: TObject);
var IconHandle: HIcon;
begin
IconHandle := SendMessage(123456, WM_GETICON, ICON_SMALL2, 0);
ImageList_AddIcon(ImageList1.Handle, IconHandle);
end;
And for example pass the 0 image index to the VirtualTreeView.
procedure TForm10.VirtualStringTree1GetImageIndex(Sender: TBaseVirtualTree;
Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex;
var Ghosted: Boolean; var ImageIndex: Integer);
begin
ImageIndex := 0;
end;

Resources