[DUG] D2006 menus and buttons
Stacey Verner
stacey at cjntech.co.nz
Wed May 17 07:54:13 NZST 2006
We use Toolbar 2000 from Jordan Russel
(http://www.jrsoftware.org/tb2k.php) with the TBX addon from Alex
Denisov (http://www.g32.org/tbx/index.html).
Toolbar 200 gives you clean Windows 2000 style menus, but with the TBX
addon you get nice theming capabilities. Downoad the TBX demo to see
what it can do.
http://www.g32.org/files/tbx/tbx-demo-2_2-alpha.zip
Note: TBX makes some modificaitons to Toolbar 2000 (make methods virtual
etc.) so you need to use Toolbar 2000 2.1.6 for the latest TBX which is
2.2 alpha (don't worry, its not alpha quality, its solid as a rock.).
Stacey
________________________________
From: delphi-bounces at ns3.123.co.nz [mailto:delphi-bounces at ns3.123.co.nz]
On Behalf Of John Bird
Sent: Tuesday, 16 May 2006 16:47
To: 'NZ Borland Developers Group - Delphi List'
Subject: [DUG] D2006 menus and buttons
I am using the XP Themes component, which is great. This gives rounded
buttons with a faint orange mouse over effect - its a standard XP
feature. However I have also see other effects which I like which I
haven't yet got:
I am wondering about the buttons and menu items widely available in
Windows XP, for instance in Outlook 2002 and in the Delphi 2006 IDE -
buttons that are invisible or flat with a mouse over blue highlight and
similar blue highlights on the main menu items current item.
Are these available in D2006?
(Up to now I have been using my own modified tjbclColorbutton which has
full control over foreground and background colour, and also mouse-over
foreground and background colour and optional bolding of text with mouse
over.....looks ok if I do say so myself. Available as giveaway if
anyone interested). The XP buttons I have seen in the IDE and Outlook I
do prefer though - as they add border effects.
John Bird
email
john at jbcl.co.nz
johnkbird at paradise.net.nz
Expert in Beyond Software
Ph land (03)384-4527 mobile (027)484-4528
92 Soleares Ave, Mt Pleasant
Christchurch
Web www.jbcl.co.nz <http://www.jbcl.co.nz/>
-----Original Message-----
From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Stefan Mueller
Sent: Tuesday, 16 May 2006 2:15 p.m.
To: 'NZ Borland Developers Group - Delphi List'
Subject: RE: [DUG] Discussion & Donation
Nhmm .. surprising. The "for" loop should evaluate lower/upper
bounds only once .. while the "while" statement always has to evaluate
the expression for each iteration. This doesn't make sense ;-/
Anyway .. a bit busy with other things rather than exploring the
"why". A russian guy cracked my licenseprotection (not that hard) last
weekend and is offering the crack to my customers on my own support
forums (
http://www.orcl-toolbox.com/forum_new2/forum_posts.asp?TID=341&PN=1 ).
Still trying to get on the good site with him(talking on email) but
patience is starting to run out.
Kind Regards,
Stefan Mueller
________________________________
From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Todd Martin
Sent: Tuesday, 16 May 2006 1:42 p.m.
To: NZ Borland Developers Group - Delphi List
Subject: Re: [DUG] Discussion & Donation
Kylie
I re-ran your timed loop with a minor change to IndexOfTodd(). I
removed the CurrentName variable.
function IndexOf_todd(AName : String; AStringArray : array of
string) : Integer;
var
NameIndex, FoundIndex : Integer;
// CurrentName : String;
begin
NameIndex := 0;
FoundIndex := 0;
while (NameIndex < length(AStringArray)) and (FoundIndex = 0)
do
begin
// CurrentName := AStringArray[NameIndex];
// if SameText(AName,CurrentName) then
if SameText(AName,AStringArray[NameIndex]) then
begin
FoundIndex := NameIndex;
end;
inc(NameIndex);
end;
Result := FoundIndex;
end;
Now compare the results
Button 1
Test Todd 1101ms
Test Stef 1142ms
Test StringList 921ms
Button 2
Test Todd 2553ms
Test Stef 2534ms
Test StringList 230ms
So Stefan's break statement is slower in the first case and adds
a negligible time saving in the second case. Personally I hate break and
continue statements, so I never use them.
The string list still looks impressive though.
But look what happens when the list size is increased from 200
to 2000 items in button 2 click.
--using TStringList
Test Todd 23023ms
Test Stef 23554ms
Test StringList 361ms
--using THashedStringList
Test Todd 22703ms
Test Stef 22632ms
Test StringList 110ms
----- Original Message -----
From: Kyley Harris <mailto:kyley at harrissoftware.com>
To: NZ Borland Developers Group - Delphi List
<mailto:delphi at ns3.123.co.nz>
Sent: Monday, May 15, 2006 2:44 PM
Subject: RE: [DUG] Discussion & Donation
Hmm your comment that doing it that way intrigued me
enough to test it. Seeing as I like to make things quicker. You were
right in certain cases.
Given the following code.
TEST 1. High iterations on a small set 5 elements
Todds loop took 150ms,
Stefans loop took 100ms
StringList took 108ms
But given test 2 high itereations on a larger list. 200
elements Which is more likely when dealing with messaging
Todds took 4125 ms
Stefans took 2641ms
Stringlist took 297ms
So I think I will stick with sorted stringlists. Because
then you can also attach a data object to it for retrieving the value,
rather than relying on the index position being the value, which I find
to be a very bad idea
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
uHssStopWatch;
{$R *.dfm}
function IndexOf_stef(AName : String; AStringArray :
array of string) : Integer;
var
i : Integer;
begin
result := -1;
for i := 0 to high(AStringArray) do
begin
if SameText(AName, AStringArray[i]) then
begin
result := i;
break;
end;
end;
end;
function IndexOf_todd(AName : String; AStringArray :
array of string) : Integer;
var
NameIndex, FoundIndex : Integer;
CurrentName : String;
begin
NameIndex := 0;
FoundIndex := 0;
while (NameIndex < length(AStringArray)) and
(FoundIndex = 0) do
begin
CurrentName := AStringArray[NameIndex];
if SameText(AName,CurrentName) then
begin
FoundIndex := NameIndex;
end;
inc(NameIndex);
end;
Result := FoundIndex;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
s:TStringList;
data:TStringList;
a1:array of string;
i:integer;
begin
initialize(a1);
s := TStringList.Create;
setlength(a1,5);
a1[0] := 'test';
a1[1] := 'testa';
a1[2] := 'testb';
a1[3] := 'testc';
a1[4] := 'testd';
s.CaseSensitive := false;
s.Sorted := true;
s.add(a1[0]);
s.add(a1[1]);
s.add(a1[2]);
s.add(a1[3]);
s.add(a1[4]);
with THssStopWatch.Start('Test Todd') do
try
for i := 0 to 1000000 do
case i mod 5 of
0: Assert(SameText('test',a1[
IndexOf_todd('TEST',a1)]));
1:
Assert(SameText('testa',a1[IndexOf_todd('TESTa',a1)]));
2:
Assert(SameText('testb',a1[IndexOf_todd('TESTb',a1)]));
3:
Assert(SameText('testc',a1[IndexOf_todd('TESTc',a1)]));
4:
Assert(SameText('testd',a1[IndexOf_todd('TESTd',a1)]));
end;
finally
Free;
end;
with THssStopWatch.Start('Test Stef') do
try
for i := 0 to 1000000 do
case i mod 5 of
0: Assert(SameText('test',a1[
IndexOf_stef('TEST',a1)]));
1:
Assert(SameText('testa',a1[IndexOf_stef('TESTa',a1)]));
2:
Assert(SameText('testb',a1[IndexOf_stef('TESTb',a1)]));
3:
Assert(SameText('testc',a1[IndexOf_stef('TESTc',a1)]));
4:
Assert(SameText('testd',a1[IndexOf_stef('TESTd',a1)]));
end;
finally
Free;
end;
with THssStopWatch.Start('Test StringList') do
try
for i := 0 to 1000000 do
case i mod 5 of
0:
Assert(SameText('test',s[s.IndexOf('test')]));
1:
Assert(SameText('testa',s[s.IndexOf('testa')]));
2:
Assert(SameText('testb',s[s.IndexOf('testb')]));
3:
Assert(SameText('testc',s[s.IndexOf('testc')]));
4:
Assert(SameText('testd',s[s.IndexOf('testd')]));
end;
finally
Free;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
s:TStringList;
data:TStringList;
a1:array of string;
i,x:integer;
begin
initialize(a1);
s := TStringList.Create;
setlength(a1,200);
s.CaseSensitive := false;
s.Sorted := true;
for i := 0 to 199 do
begin
a1[i] := 'test'+inttostr(i);
s.add(a1[i]);
end;
with THssStopWatch.Start('Test Todd') do
try
for i := 0 to 100000 do
x := IndexOf_Todd(a1[i mod 200],a1);
finally
Free;
end;
with THssStopWatch.Start('Test Stef') do
try
for i := 0 to 100000 do
x := IndexOf_Stef(a1[i mod 200],a1);
finally
Free;
end;
with THssStopWatch.Start('Test StringList') do
try
for i := 0 to 100000 do
x := s.IndexOf(a1[i mod 200]);
finally
Free;
end;
end;
end..
________________________________
From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Stefan Mueller
Sent: Monday, 15 May 2006 1:36 p.m.
To: 'NZ Borland Developers Group - Delphi List'
Subject: RE: [DUG] Discussion & Donation
Hi Todd,
Here is how I usually write such scanning loops. Should
be a bit faster than yours and less confusing (less juggling around with
variables) ... and less error prone (raises exceptions if AName can't be
found):
function IndexOf(AName : String; AStringArray : array of
string) : Integer;
var
i : Integer;
begin
result := -1;
for i := 0 to high(AStringArray) do
begin
if SameText(AName, AStringArray[i]) then
begin
result := i;
break;
end;
end;
if result := -1 then
raise exception.create('String '+ AName +' not found
in array!');
end;
Regards,
Stefan
________________________________
From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Todd Martin
Sent: Monday, 15 May 2006 12:55 p.m.
To: NZ Borland Developers Group - Delphi List
Subject: Re: [DUG] Discussion & Donation
3) Okay. Good point. I didn't pick up on this, as you
hadn't marked the procedures as virtual, but I guess that can always be
added later without breaking existing code.
4) Have you considered RegisterIntegerConsts() for
converting message integers to text?
Or failing that I often use something like this
type
TMessageType = (mtUnknown,mtChange,mtSave,mtLoad)
const
MessageTypeToText : array[TMessageType ] of string =
('Unknown','Change','Save','Load');
That way, whenever I add an enumeration, I have to also
add an equivalent string description.
To convert the text back to an enumerated type, I just
use a standard procedure to find the index of the text in the array. By
making the first enumeration unknown/default I always get a meaningful
result when casting the integer back to a type.
Type :=
TMessageType(IndexOf(MessageText,MessageTypeToText);
function IndexOf(AName : String; AStringArray : array of
string) : Integer;
var
NameIndex, FoundIndex : Integer;
CurrentName : String;
begin
NameIndex := 0;
FoundIndex := 0;
while (NameIndex < length(AStringArray)) and
(FoundIndex = 0) do
begin
CurrentName := AStringArray[NameIndex];
if SameText(AName,CurrentName) then
begin
FoundIndex := NameIndex;
end;
inc(NameIndex);
end;
Result := FoundIndex;
end;
However, I see your point about streaming numbers to XML
Todd.
----- Original Message -----
From: Kyley Harris
<mailto:kyley at harrissoftware.com>
To: NZ Borland Developers Group - Delphi List
<mailto:delphi at ns3.123.co.nz>
Sent: Monday, May 15, 2006 11:05 AM
Subject: RE: [DUG] Discussion & Donation
Thank.
Point 1/ Fair enough
Point 2/ correct . I had written that bit early
on before I realized I could break it down correctly with TMethdo.code
and TMethod.Data and I haven't fully refactored it yet. I've removed it
now.
Point 3/ Because you cant do anything with a
bunch of procedures. Ie it is not portable. Class functions can be
overridden. You don't need to use TNotifier.AddListener,
You could have for example add a meta class and
a classvariable to the global, Var NotifierClass:TNotiferClass =
TNotifer. I then use this for accessing the functions and down the track
in some cases or applications you may wish to modify how it works,
without effecting All the other apps by simply changing the class
pointer. static procedures do not lend themselves to upgrading software
without breaking old software and this is one of the most powerful uses
of static classes as libraries that many people in Delphi don't take
advantage of.
Point 4. I use constants, so I don't have typos
in message names (except for demos). And I use SameText, so they are not
case-sensitve. I do this purely because I prefer debugging meaningful
strings rather than integers. It also makes It easier when dealing with
storage of types etc in human readable XML. So that's why I do it. Many
books use integers. I just don't like it. Also. A lot of my
infrastructor permits streaming of the entire message, and the data
object over TCP as part of the notification. I prefer unknown apps
receiving this to get a meaningful 'CLIENT_OBJECT_UPDATED' rather than 3
;)
The StringList is indexed, and its very fast,
I've tested with thousands of binds with no real time effect. The work
done by listeners is the time issue. Not the lookup.
I used to have an observer and observed class
system too. And it was really annoying when you had to make changes. I
even wrote a plugin for the IDE to convert an object into a listener etc
by inserting all the code for me. So that took the hassle out. But I
prefer the decoupled method.
Thanks for the feedback.
________________________________
From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Todd Martin
Sent: Monday, 15 May 2006 10:38 a.m.
To: NZ Borland Developers Group - Delphi List
Subject: Re: [DUG] Discussion & Donation
Well you said you wanted feedback, so I hope
this isn't too picky.
1) I find the inconsistent use of variable
prefixes a bit annoying. I tend to stick with
F- class field
A - method parameter
P - pointer
and no prefixes for class properties or local
variables.
2) TListenerItem.FListener is redundant, since
the information it contains is already held in TListenerItem.FBindMethod
It could have been implemented as a function,
but I don't see why it is needed.
As usual, redundancy leads to complication
elsewhere
eg. if
(MethodEquals(TMethod(Items[i].FBindMethod) ,TMethod(ABinder))) and
(Items[i].FListener = AListener) then
the second condition is automatically satisfied
by the first condition.
3) Why define a TNotifier class when all its
methods are static? They could be defined as simple procedures/functions
in the unit.
4) I'm not keen on the idea of passing a string
parameter to identify the message type, for the simple reason that
spelling mistakes can arise and you're never really sure at compile time
what "messages" are being broadcast/handled. This problem can of course
be ameliorated through the use of constants, but I prefer the use of an
enumerated type.
Having said all that. I like the loose coupling
of your solution. I have implemented the observer pattern previously
with the lists maintained internally by the objects involved, so
removing the need to inherit from a particular class is a great
improvement. I hadn't considered that option before. Of course you do
pay a miniscule performance penalty in having to find the correct list
for the object. Perhaps the use of THashedstringlist could mitigate this
further.
Todd.
----- Original Message -----
From: Kyley Harris
<mailto:kyley at harrissoftware.com>
To: NZ Borland Developers Group - Delphi
List <mailto:delphi at ns3.123.co.nz>
Sent: Wednesday, May 10, 2006 3:36 PM
Subject: [DUG] Discussion & Donation
In an attempt to create some more
interesting discussion, other than talking about the crud IDE issues, I
am donating some useful code for discussion, rather than for help. Feel
free to keep/use the code if you want to. Be mean if you feel the need
(but if your personal then watch out ;) Hopefully this may introduce a
discussion where people learn something, or are able to contribute some
interesting ideas/information about the topic (which is object
notification and observation).
In the attached zip is a sample
application showing a basic, but horrible use of the class in
uNotifier.pas. TNotifer is a class I wrote a long while back which
loosely follows an observer pattern for allowing one-to-many
observations between an object, and a bunch of interested object.
The main difference this class has
between many observer patterns is that you do not have to modify
existing classes, or sub-class anything, or instantiate anything in your
existing objects. The TNotifier handles all the bindings, you just need
to implement one listening event on each listening object.
The sample app is a corny app that makes
child forms (button click) update a memo based on changes to the memo on
the main form. God help me if I ever did anything as potentially
unthread safe as that in a real app.
Fun fun fun.... (I hope the app
compiles. You never know with these tricky little sample apps)
________________________________
_______________________________________________
Delphi mailing list
Delphi at ns3.123.co.nz
http://ns3.123.co.nz/mailman/listinfo/delphi
________________________________
Internal Virus Database is out-of-date.
Checked by AVG Free Edition.
Version: 7.1.385 / Virus Database:
268.5.1/327 - Release Date: 28/04/2006
________________________________
_______________________________________________
Delphi mailing list
Delphi at ns3.123.co.nz
http://ns3.123.co.nz/mailman/listinfo/delphi
________________________________
Internal Virus Database is out-of-date.
Checked by AVG Free Edition.
Version: 7.1.385 / Virus Database: 268.5.1/327 -
Release Date: 28/04/2006
________________________________
_______________________________________________
Delphi mailing list
Delphi at ns3.123.co.nz
http://ns3.123.co.nz/mailman/listinfo/delphi
________________________________
Internal Virus Database is out-of-date.
Checked by AVG Free Edition.
Version: 7.1.385 / Virus Database: 268.5.1/327 - Release
Date: 28/04/2006
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://ns3.123.co.nz/pipermail/delphi/attachments/20060517/f189854d/attachment-0001.html
More information about the Delphi
mailing list