[DUG] D2006 menus and buttons

John Bird johnkbird at paradise.net.nz
Tue May 16 16:46:34 NZST 2006


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
<http://www.orcl-toolbox.com/forum_new2/forum_posts.asp?TID=341&PN=1> &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  <mailto:kyley at harrissoftware.com> Harris 

To: NZ Borland  <mailto:delphi at ns3.123.co.nz> Developers Group - Delphi List


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  <mailto:delphi at ns3.123.co.nz> Developers Group - Delphi List


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  <mailto:delphi at ns3.123.co.nz> Developers Group - Delphi List


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/20060516/bcfa24a5/attachment-0001.html


More information about the Delphi mailing list