讲述如何开发一个控件,很有价值(七)

原创 2001年07月20日 14:46:00

SUCCESS - (Nearly...)
 

I think you'll agree we are pretty close. There is just a little bit of flicker. This flicker is the SelStart jumping the Cursor position around the text. We need to hide this. This "Cursor" is also known as a Caret. Looking throught Win32.Hlp again we find the lovely, and appropriately named, HideCaret() function.

Lets try this then: everytime we change the value of MyRe.SelStart lets call HideCaret(MyRe.Handle) immediately before.

I'll be kind - that doesn't work. I tried 2 x HideCaret(MyRe.Handle), and it still didn't work. Neither did three,four or 25x. So close - but yet - so far. I think its time for another Delphi Rule:

DELPHI RULE #5 - If you bother to get your way through the atrocious index of the Win32.HLP file to find what you are looking for - make sure you really read what you found properly!

The key was the last paragraph in the description of not HideCaret but ShowCaret (which I had also read as I thought we were going to need it, especially to reverse my 25x HideCaret()). You also need another Delphi Rule to understand it:
 

The caret is a shared resource; there is only one caret in the system. A window should show a caret only when the window has the keyboard focus or is active. 

DELPHI RULE #6 - Everything (basically) is a Window

You see the RichEdit is a windows control and is also.. in a weird sense.. a window. It has a Handle, which is why HideCaret would accept it. So re-reading the last line again we get:
 

The caret is a shared resource; there is only one caret in the system. A [RichEdit] should show a caret only when the [RichEdit] has the keyboard focus or is active. 

So - in the end - we're back to were we started - we have to disable the RichEdit to stop the final bit of flickering. This also (co-incidentially) means that EM_HIDESELECTION is not needed anymore (if HideSelection is set properly during Design time). So in the end everyone gets 10/10 for marks!

ASH Version 0.9b
 
 

procedure TForm1.RichEdit1Change(Sender: TObject);

var

SaveOnChangeIn: TNotifyEvent; 


WasSelStart,WasRow,Row,BeginSelStart,EndSelStart: Integer;
MyRe : TRichEdit;
MyPBuff: array[0..255] of char;
MyTokenStr:string;
MyTokenState:TTokenState;
MyRun:PChar;
MySelStart: Integer; 

begin

MyRe := TRichEdit(Sender);

SaveOnChangeIn := MyRe.OnChange;
MyRe.OnChange  := nil;

MyRe.Enabled   := False;

WasSelStart      := MyRE.SelStart;
WasRow            := MyRE.Perform(EM_LINEFROMCHAR, MyRE.SelStart, 0);
Row                    := WasRow;
BeginSelStart    := MyRe.Perform(EM_LINEINDEX, Row, 0);
EndSelStart       := BeginSelStart + Length(MyRE.Lines.Strings[Row]);

StrPCopy(MyPBuff,MyRE.Lines.Strings[Row]);
MYPBuff[Length(MyRE.Lines.Strings[Row])] := #0; 
MySelStart   := BeginSelStart;
MyRun         := MyPBuff;

while(MyRun^ <> #0) do
begin

MyRun := PasCon.GetToken(MyRun,MyTokenState,MyTokenStr);

MyRE.SelStart := MySelStart;
MyRE.SelLength := Length(MyTokenStr);

If MyRE.SelAttributes.Name <> PasCon.FParseFont[MyTokenState].Name then MyRE.SelAttributes.Name := PasCon.FParseFont[MyTokenState].Name;

If MyRE.SelAttributes.Color <> PasCon.FParseFont[MyTokenState].Color then MyRE.SelAttributes.Color := PasCon.FParseFont[MyTokenState].Color;

if MyRE.SelAttributes.Style <> PasCon.FParseFont[MyTokenState].Style then MyRE.SelAttributes.Style := PasCon.FParseFont[MyTokenState].Style;

MySelStart := MySelStart + Length(MyTokenStr);

end;

MyRE.SelStart          := WasSelStart;
MyRE.SelLength      := 0;
MyRe.OnChange     := SaveOnChangeIn;
MyRe.Enabled         := True;

MyRe.SetFocus;

end;

Towards - ASH Version 1.0b

Couple of problems with the last version if you try it out for size:

  1. Its slightly inefficient in that everytime SelAttributes is changed it forces a repaint of the same token in the control. We should instead use some variable (e.g var DoFormat:Boolean) to decided if we need to reformat, and then check the value of DoFormat at the end of this checking, and do it all then by a simple SelAttribute.Assign(FParseFont[MyTokenState]). This means we can also change the seperate "if" statements to a single if ... then .. else .. if ... then .. else which should code faster - especially if you put the various test situations in the order of likeliness to occur (e.g font changes less frequently than the color, so should be further down the if..else..if)

  2.  
  3. For some reason if you type a {style comment} on a line, after about 4-7 characters it reverts to different colours. I can't seem to work out yet why this happens - but I understand why its not being picked up. SelAttributes returns the value of the "initial" styling of the entire selected block. So if you select text which starts off black and then becomes blue, SelAttributes.Color will equal clBlack. We must also examine SelAttributes.ConsistentAttributes to ensure that the entire selection is consistent in the way it is highlighted. If it isn't - then we want to force it to be rehighlighted - its obviously not in the correct format.

  4.  
  5. Multi-line comments are a big pain e.g { words <CR> word <CR> words }. I don't have them in my 4GL so I didn't need to fix this sort of problem. However I do have muti-line strings - so I need to be able to string strings across many lines. The trouble is we have to code to program over a number of lines - but have a look at what happens in Delphi when you place a "{" anywhere in the code. The highlighting can force a repaint of the entire 2,000,000 lines of text in the control. We could catch that situation - ie if the last token on the line is a tsComment and it doesn't end in '}' we could increase SelLength until it did or we reach the end of the RichEdit.Lines. (That basically what the tokeniser does anyway with all that inc(Run).)

  6.  


     
     

    That easy. But what happens if you then delete the "{"? You need to go forward 2,000,000 lines and put the highlighting back again? We could decide to keep going until the if...then..else..list didn't set DoFormat := True. But what happens if we're in a colour mode were Comment highlighting style  = KeyWord highlighting style. We would stop prematurely. So this "logic" wont help in all situations.
     

  7. You can still get the "someone is chasing you effect" - except now its "someone is fleeing from you" effect. It happens when you have (* This is a comment *) and delete the first *-character. The control takes an appreciable time to rehighlight the text.

  8.  
  9. While looking for a fix for the last problem, I remembered the Richedit.Lines.BeginUpdate function. But that didn't help either. What we need is a Richedit.BeginUpdate. What would that do? It would increase an internal counter by one everytime it was called. RichEdit.EndUpdate would do the opposite. Then we would create our own WM_PAINT message handler. This is received everytime Windows wants the control to repaint a portion of itself. If we catch this message then we can stop processing of these message until the internal counter = 0 again. Then, and only then, will the Control repaint itself - ditching we would hope most of the intervening steps.

  10.  
  11. Fixing the mult-line comments:

  12.  


     
     

    My current idea is to use the RichEdit.Lines.Object to store the TokenType of the first token on each line. This way we could easily know how far we need to go when re-hightlighting multi-line comments. Initially this would be set to nil. I think this will work.

    [Editor update: This didn't actually work - as the RichEdit.Lines.Object isn't implemented in TRichEdit control. It is
    always nil regardless of what you assigned to it]
     

  13. Upgrading to RichEdit98:

  14.  


     
     

    I'm also in the process of updating to the RichEdit98 components for Delphi 3.0-4.0. version 1.34 Author Alexander Obukhov, Minsk, Belarus. This control has a number of advances on the standard RichEdit control that ships with Delphi. Included in this are:

  15. BeginUpdate,EndUpdate
  16. Independant background colours
  17. Margins
  18. Hotspots
  19. (Source code in full)

Anyway I hope you have enjoyed the adventure.I'm sorry if not all the examples compile as written. They may need some fixing to compile if you copy straight from the Browser into the Delphi Editor. Please send any comments to jonhd@hotmail.com.

Jon HogDog

讲述如何开发一个控件,很有价值

 The Unofficial Newsletter of Delphi Users by Robert Vivrette Automatic Syntax Highlighting Using a ...
  • mysine
  • mysine
  • 2001年07月20日 14:40
  • 642

讲述如何开发一个控件,很有价值(四)

To start with I used the Edit1 Control to display the results of all these variables. I then tried m...
  • mysine
  • mysine
  • 2001年07月20日 14:44
  • 385

讲述如何开发一个控件,很有价值(五)

To start with I used the Edit1 Control to display the results of all these variables. I then tried m...
  • mysine
  • mysine
  • 2001年07月20日 14:44
  • 422

讲述如何开发一个控件,很有价值(二)

would be divided up into: proceduretsKeyWord tsSpaceTForm1tsIdentifier.tsSymbolFormCreatetsIdentifie...
  • mysine
  • mysine
  • 2001年07月20日 14:42
  • 559

讲述如何开发一个控件,很有价值(三)

blank.rtf - empty -so I could see the "plain" header line {/rtf1/ansi/deff0/deftab720{/fonttbl{/f0/f...
  • mysine
  • mysine
  • 2001年07月20日 14:43
  • 636

讲述如何开发一个控件,很有价值(六)

 ASH - Automatic Syntax highlight (Attempt 2) [Please note: I have my Delphi Editor colors set-to th...
  • mysine
  • mysine
  • 2001年07月20日 14:45
  • 541

一个很有价值的研究成果

一个跨学科团队今年完成了一项对资源稀缺状况下人的思维方式的研究,结论是:穷人和过于忙碌的人有一个共同思维特质, 即注意力被稀缺资源过分占据,引起认知和判断力的全面下降。这项研究是心理学、行为...
  • SHIZHONGYUO
  • SHIZHONGYUO
  • 2014年08月28日 21:01
  • 542

如何开发一个Windows控件

本文是基于Windows平台下的dot Net环境上的Form程序,思想可能也适用于其他场合。算是对过去开发经验的总结,保不齐有些内容是胡扯,勿怪。一.背景介绍  很多时候做winForm程序...
  • yuanhuiqiao
  • yuanhuiqiao
  • 2009年11月18日 14:18
  • 5815

一个很有借鉴价值的编程故事

一周七天,每天学一点在线课程,哪怕一次只有半小时,只要坚持不懈,你就能感觉到自己今天做出的决定是正确的。请记住:只要有耐心,任何能干的人都能成为绝世coder,当然也包括你。 这是一个真实的故事...
  • u013244517
  • u013244517
  • 2014年11月26日 23:01
  • 263

简单的游戏界面设计

先看效果 自己做那还是游戏啊 失败失败 自己只有一点点图片 图片多可以自己去替换下面的文件夹中的图片 主要的代码 using UnityEngine; using System.Collectio...
  • u012487582
  • u012487582
  • 2015年02月13日 22:43
  • 616
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:讲述如何开发一个控件,很有价值(七)
举报原因:
原因补充:

(最多只允许输入30个字)