Tuesday, October 27, 2009

Dell Latitude XT2 XFR Rugged Tablet



Dell just introduced the Latitude XT2 XFR; the rugged version of the XT2 multitouch tablet machine I've mentioned a few times before only with a name that is more of a mouth full. So for those of you waiting for a rugged machine before jumping into multitouch wait no longer. The short spec list is 12 inch multitouch display, 5lbs and 1.5 inches thick and mil spec badassness! It also comes with Vista. I guess Windows 7 isn't rugged certified? Just as well, first thing I do when I get a new machine is format it anyway. For all the awesomeness it'll set you back some serious coin. The base model is $3,600 but I loaded one up for just under $5k! I don't think I can justify one of these to Nick, but damn!

Monday, October 26, 2009

Dell SX2210T - Multitouch Monitors

The first standalone multi-touch monitor is the SX2210T. Looks pretty awesome for those that want to add a multi-touch monitor tho an existing computer.

Updated: Link goes to the product page (which is up now) rather than the shopping cart.

Thursday, October 15, 2009

Delphi Tips and Tricks - Additional Files

One of the most useful features in the Delphi IDE is CTRL + F12, or the "Search for units" dialog. One of the problems we encounter is we often want to look at files that are not in any of the opened projects. So a hidden registry key was created to add a list of files to the "Search for units" dialog. You can specify the text file by creating a string value under HKEY_CURRENT_USER\Software\CodeGear\BDS\\Globals named "AdditionalViewFiles". Set the string to the file you want the IDE to load. Note that IDE environment variables can be used here. For Example I have $(TP) set to "c:\source" and the AdditionalViewFiles string value is set to $(TP)\AdditionalViewFiles.txt.

The text file format is name value pairs like any other INI file where the name is the short name you want to see and the value is the full path to the file (and this will expand IDE environment variables as well). For example:

Foo.pas=$(TP)\somedirectory\Foo.pas

Monday, October 12, 2009

Sony Touchscreen Computer/TV



From the specs I think this Sony Vaio L beats pants off the HP Touchsmart. It's got glass, HDMI input, 1080p (I'm not sure if this is the computer resolution as well) Blue-ray, NVIDIA graphics and the monitor can be used as a TV without turning on the computer. I'm not sure what the other computer specs are, but the mouse does look horrible. It also appears that the DVD drive isn't a slot loading drive so that's a bit odd. Check out Sony website for more information.

Wednesday, October 7, 2009

Touch Demo Part III

Previously I posted Part I and Part II of the Touch Demo.

This time around we will add the glowing spots and the basic handling of the WM_TOUCH message. Add Generics.Collections to your uses and copy the following code to your unit:


type
TGlowSpot = class
public
X, Y, Radius: Integer;
Alpha: Extended;
FadeIn: Boolean;
Color: TColor;

public
constructor Create(AParent: TWinControl);

procedure Paint(Canvas: TDirect2DCanvas);
end;

TGlowSpotList = class(TList<TGlowSpot>);
...
implementation

{$R *.dfm}

procedure PaintGlow(Canvas: TDirect2DCanvas; Alpha: Single;
X, Y, Radius: Integer; Color: TColor);
var
Stops: array[0 .. 1] of TD2D1GradientStop;
Gradient: ID2D1GradientStopCollection;
BrushProperties: TD2D1RadialGradientBrushProperties;
RadialBrush: ID2D1RadialGradientBrush;
Brush: TDirect2DBrush;
begin
Stops[0].position := 0;
Stops[0].Color := D2D1ColorF(Color, Alpha);
Stops[1].position := 1;
Stops[1].Color := D2D1ColorF(Color, 0);
Canvas.RenderTarget.CreateGradientStopCollection
(@Stops[0], Length(Stops), D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP,
Gradient);
BrushProperties.center := D2D1PointF(X, Y);
BrushProperties.gradientOriginOffset.X := 0;
BrushProperties.gradientOriginOffset.Y := 0;
BrushProperties.radiusX := Radius;
BrushProperties.radiusY := Radius;
Canvas.RenderTarget.CreateRadialGradientBrush
(BrushProperties, nil, Gradient, RadialBrush);
Brush := TDirect2DBrush.Create(Canvas);
Brush.Handle := RadialBrush;
Canvas.Pen.Color := clNone;
Canvas.Brush := Brush;
Canvas.Ellipse(X - Radius, Y - Radius,
X + Radius, Y + Radius);
end;

{ TGlowButton }

constructor TGlowSpot.Create(AParent: TWinControl);
begin
inherited Create;
Alpha := 1;
Radius := 80;
FadeIn := False;
Randomize;
Color := RGB(Random(255), Random(256), Random(256));
end;

procedure TGlowSpot.Paint(Canvas: TDirect2DCanvas);
begin
PaintGlow(Canvas, Alpha, X, Y, Radius, Color);
end;

Add a local to hold a lists of TGlowSpots to your form then create it and free it:

TTouchForm = class(TForm)
...
private
FSpots: TGlowSpotList;
...
constructor TTouchForm.Create(AOwner: TComponent);
begin
inherited;
FCanvas := TDirect2DCanvas.Create(Handle);
FSpots := TGlowSpotList.Create;
end;

destructor TTouchForm.Destroy;
begin
FCanvas.Free;
FSpots.Free;
inherited;
end;

Update the Paint routine to paint the spots:

procedure TTouchForm.Paint;
var
Spot: TGlowSpot;
begin
Canvas.BeginDraw;

try
// Clear Background
Canvas.RenderTarget.Clear(D2D1ColorF(clBlack));

for Spot in FSpots do
Spot.Paint(Canvas);

// FPS
Canvas.Font.Color := clWhite;
Canvas.Brush.Color := clNone;
Canvas.Font.Size := 14;
Canvas.TextOut(10, 10, FloatToStrF(FFPS, ffFixed, 2, 2) + ' FPS');
finally
Canvas.EndDraw;
end;
end;

Modify the Update method to fade the spots:

procedure TTouchForm.Update;
var
Spot: TGlowSpot;
begin
Inc(FFrames);

if GetTickCount - FStartTime >= 1000 then
begin
FFPS := FFrames;
FFrames := 0;
FStartTime := GetTickCount;
end;

for Spot in FSpots do
begin
if Spot.FadeIn then
Spot.Alpha := Spot.Alpha + 0.012
else
Spot.Alpha := Spot.Alpha - 0.012;

if Spot.Alpha < 0.3 then
begin
Spot.FadeIn := True;
Spot.Alpha := 0.4
end
else if Spot.Alpha > 1 then
Spot.FadeIn := False;
end;
end;

And last handle the WM_TOUCH message:

TTouchForm = class(TForm)
...
procedure WMTouch(var Message: TMessage); message WM_TOUCH;
...
procedure TTouchForm.WMTouch(var Message: TMessage);

function TouchPointToPoint(const TouchPoint: TTouchInput): TPoint;
begin
Result := Point(TouchPoint.X div 100, TouchPoint.Y div 100);
PhysicalToLogicalPoint(Handle, Result);
end;

var
TouchInputs: array of TTouchInput;
TouchInput: TTouchInput;
Handled: Boolean;
Point: TPoint;
Spot: TGlowSpot;
begin
Handled := False;
SetLength(TouchInputs, Message.WParam);
GetTouchInputInfo(Message.LParam, Message.WParam, @TouchInputs[0],
SizeOf(TTouchInput));
try
for TouchInput in TouchInputs do
begin
Point := TouchPointToPoint(TouchInput);
Spot := TGlowSpot.Create(Self);
Spot.X := Point.X;
Spot.Y := Point.Y;
FSpots.Add(Spot);
end;

Handled := True;
finally
if Handled then
CloseTouchInputHandle(Message.LParam)
else
inherited;
end;
end;

If you ran your app at this point you wouldn't get any touch messages. You need to call RegisterTouchWindow and UnregisterTouchWindow. I find it easiest to call UnregisterTouchWindow in the FormClose:

procedure TTouchForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
UnregisterTouchWindow(Handle);
end;

and to call RegisterTouchWindow in the CreateWnd:

procedure TTouchForm.CreateWnd;
begin
inherited;
RegisterTouchWindow(Handle, 0);
end;

Now when you run your app and press the screen you'll get glowing spots wherever you touch the screen. Depending on your hardware 1, 2 or more at a time.

UPDATE: There is a memory leak in this program because I didn't want to delete any code for the next step.

UPDATE: Fixed HTML to make TGlowSpotList = class(TList) actually read TGlowSpotList(TList<TGlowSpot>);

Tuesday, October 6, 2009

How to Write Applications Useful for Color Blind Users

We Are Color Blind is an online repository of information for anyone involved with creating content for the web and making it accessible to the color blind. This is a great resource that I feel is useful for anyone creating content or building applications, not just for the web.

The Quick Tips is probably a good place to start.

UPDATE: Fixed links.

Touch Demo Part II

Last week I posted Touch Demo Part I. This is the continuation to that series.

In Part I I demonstrated how to add the Direct2D Canvas to a form. This step is another foundation step that will add frames to your application by adding a timer.

At the moment the form looks like this:


type
TTouchForm = class(TForm)
private
FCanvas: TDirect2DCanvas;
protected
procedure Paint; override;
procedure Resize; override;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;

public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;

property Canvas: TDirect2DCanvas read FCanvas
write FCanvas;
end;

Add the following private fields to keep track of the frames:

FFPS: Integer;
FFrames: Integer;
FStartTime: Cardinal;

and initialize them in the FormShow:

procedure TTouchForm.FormShow(Sender: TObject);
begin
FFPS := 0;
FFrames := 0;
FStartTime := GetTickCount;
end;

Next update the Paint function:

procedure TTouchForm.Paint;
begin
Canvas.BeginDraw;
try
// Clear Background
Canvas.RenderTarget.Clear(D2D1ColorF(clBlack));

// FPS
Canvas.Font.Color := clWhite;
Canvas.Brush.Color := clNone;
Canvas.Font.Size := 14;
Canvas.TextOut(10, 10, FloatToStrF(FFPS, ffFixed, 2, 2) + ' FPS'); finally
Canvas.EndDraw;
end;
end;

And then add a protected Update method that updates the frames:

procedure TTouchForm.Update;
begin
Inc(FFrames);

if GetTickCount - FStartTime >= 1000 then
begin
FFPS := FFrames;
FFrames := 0;
FStartTime := GetTickCount;
end;
end;

Last, drop a TTimer on your form and set the interval to 10. Also set the form's Align to alClient and the BorderStyle to bsNone.

Next create the OnTimer event and simply call two functions, Update and Paint:

procedure TTouchForm.Timer1Timer(Sender: TObject);
begin
Update;
Paint;
end;

At this point your form should look like this:

type
TTouchForm = class(TForm)
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure FormShow(Sender: TObject);
private
FCanvas: TDirect2DCanvas;
FFPS: Integer;
FFrames: Integer;
FStartTime: Cardinal;
protected
procedure Update;
procedure Paint; override;
procedure Resize; override;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;

public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;

property Canvas: TDirect2DCanvas read FCanvas
write FCanvas;
end;

Monday, October 5, 2009

HP Touchsmart

If you buy a Touchsmart be sure to spend the extra duckets to get the NVIDIA graphics card. It comes with an NVIDIA GeForce 9300M which is a pretty smokin' card and you'll be much happier.