Sign in to follow this  
  • entries
    23
  • comments
    28
  • views
    20190

Closing In

Sign in to follow this  

196 views

Righty-o! I'm closing in on getting the control finished up. Progress moves a bit faster when one actually works on the project. Granted, I'm only working a little on it, but that is a little more than what I was. Just gotta keep trucking. Etc.

Horizontal scrolling is now implemented in rnfnCodeLite. It has yet to be tested for large quantities of lines, but the tests I performed on a few lines seems to indicate that it is working quite nicely. I'm sure it could be optimized, but it increases/decreases the horizontal scrollbar's maximum value appropriately for now. A few 'weird' bits that I may or may not work on, depending. For example, it takes a few characters before the scrollbar provides any scrolling. This is possibly due to my having it's LargeChange value set to text area's width, which seems a bit large. I think Visual Studio's is using something like 1/5th of the width? Or maybe it is just an arbitrary number. Not sure, but it definately seems to not be the text area's width.

Thinking about toying around with allowing the code editor's contents to be printed via System.Drawing.Printing.PrintDocument, but currently in a bit of a rut regarding GDI and the resolution of the page being printed. If I don't find a nice workaround, I may just ditch it, as it isn't essential, but would be really nice.

The next step I've got to making it a nice little text editor is implementing text selection. I've been toying with various ideas for this in my head, and I'm pretty sure I'm going to settle for multiple selections. This would allow for quick find/replace that I'll also probably be implementing. The only drawback to using it for this would be if the user hit the backspace key, though I suppose a quick CTRL+Z would solve that.

Speaking of CTRL+Z, I also have to implement an Undo/Redo system. Through numerous mental evolutions, I've decided it is going to be selection based. If there is no text selected, it'd operate just like a regular Undo/Redo would. However, if text were selected, it would perform the most recent Undo/Redo(as appropriate) for that text and only in regards the selected text. Previously, it was going to Undo/Redo the entire operation if it had occurred on a line that was in the text selection, but upon explaining this to a friend, he pointed out that it would get a bit confusing. So, if I do go this route (there is always the chance it'll prove not worth the effort), it is likely that only part of the action would be redone/undone. For example, if I'd pasted "foobarbiz" in the middle of "tutu", highlighted "barbiztu", and then hit CTRL+Z, the result would be the text "tufootu" with the next undo action being to remove the "foo" bit. In theory, I don't think it'll be too bad going backwards through the collection to find the appropriate area of text, nor altering the undo/redo action if it is only a partial undo/redo. The problem I'm currently thinking on is whether to allow for infinite undo/redo. I haven't done any testing on it, but surely it isn't going to suffice just storing it all in memory? And I can't really store older parts of it in a file since any performance benefits would be lost with this "partial-selected-text" undo/redo scheme. Hmmm.....

So, yeah. When I get those two done, debugged, and tweaked, all I should have to do is add ContextMenu support and it'll be a nice little text editor. The only other major thing after that, that I can recall, will be to implement a keyword syntax highlighter to go along with the default text 'engine' as two in-package CodeEngine's. Hopefully others'll take an interest and start writing better CodeEngine's, whether they are all encompassing or just for a specific language. For this post's code snippet, I think I'll show what the CodeEngine class is so far, from Reflector's point of view:
public abstract class CodeEngine
{
// Fields
internal rnfnCodeLiteData data;

// Methods
public CodeEngine(rnfnCodeLite master);
public virtual bool CanPrint();
public abstract int CaretCharOffset(Graphics graphics, Line line, int caretPixelOffset);
public abstract int CaretPixelOffset(Graphics graphics, Line line, int caretCharOffset);
public abstract void Draw(Graphics graphics, Line line, Rectangle area);
protected int FindAfter(Line line, string query);
protected int FindBefore(Line line, string query);
protected Line GetLine(int linenumber);
protected Line[] GetLines(int lineNumber, int lines);
public virtual void MouseClick(rnfnCodeLite control, MouseEventArgs e);
public virtual void MouseDoubleClick(rnfnCodeLite control, MouseEventArgs e);
public virtual void MouseHover(rnfnCodeLite control, EventArgs e);
public virtual void OnHelpRequested(HelpEventArgs hevent);
private void OnLineChanged(int lineNumber);
public virtual void Print(CodeEnginePrintDocument document, PrintPageEventArgs e);
public virtual void Replacing(ref int startLine, ref int startOffset, ref int length, ref string text);
protected void SetCollapseData(Line line, int linesToSkip);
protected void SetIcon(Line line, Icon icon);

// Properties
protected Color BackColor { get; set; }
protected Font Font { get; set; }
protected Color ForeColor { get; set; }
protected int LineCount { get; }
protected bool ShowCollapseArea { get; set; }
protected bool ShowColoredIndicator { get; set; }
protected bool ShowIconGutter { get; set; }
protected bool ShowLineNumbers { get; set; }
protected TextFormatFlags TextFormatFlags { get; }

// Nested Types
public abstract class CodeEngineData
{
// Methods
protected CodeEngineData();
}

[StructLayout(LayoutKind.Sequential)]
public struct Line
{
internal LinkedListNode line;
internal Line(Text.Line data);
internal Line(LinkedListNode line);
public int CollapseData { get; }
public Icon Icon { get; }
public string Text { get; }
public CodeEngine.CodeEngineData Data { get; set; }
}
}


Also, if you're still sticking around and are interested, here is the DefaultText bit, a 'basic' implementation of a CodeEngine that acts as the default engine for the control:
public class DefaultText:CodeEngine
{
public DefaultText(rnfnCodeLite master):base(master){}
public override void Draw(Graphics graphics,Line line,Rectangle area)
{
TextRenderer.DrawText(graphics,line.Text,Font,area,ForeColor,TextFormatFlags);
}
public override bool CanPrint()
{
return true;
}
public override int CaretCharOffset(Graphics graphics,CodeEngine.Line line,int caretPixelOffset)
{
//Past the end
if(caretPixelOffset>TextRenderer.MeasureText(graphics,line.Text,Font,new Size(),TextFormatFlags).Width)
return line.Text.Length;

//At the start
if(caretPixelOffset<0)
return 0;

//Iterative Binary Search
int low=0;
int high=line.Text.Length;
int mid=0;

while(low {
mid=low+(high-low)/2;

if(TextRenderer.MeasureText(graphics,line.Text.Substring(0,mid),Font,new Size(),TextFormatFlags).Width low=mid+1;
else
high=mid;
}

int middleDiff=Math.Abs(TextRenderer.MeasureText(graphics,line.Text.Substring(0,mid),Font,new Size(),TextFormatFlags).Width-caretPixelOffset);
int lowerDiff=middleDiff;
try{lowerDiff=Math.Abs(TextRenderer.MeasureText(graphics,line.Text.Substring(0,mid-1),Font,new Size(),TextFormatFlags).Width-caretPixelOffset);}catch(Exception exception){}
int higherDiff=middleDiff;
try{higherDiff=Math.Abs(TextRenderer.MeasureText(graphics,line.Text.Substring(0,mid+1),Font,new Size(),TextFormatFlags).Width-caretPixelOffset);}catch(Exception exception){}

if(lowerDiff return mid-1;
if(higherDiff return mid+1;
return mid;
}
public override int CaretPixelOffset(Graphics graphics,Line line,int caretCharOffset)
{
if(caretCharOffset<=line.Text.Length)
return TextRenderer.MeasureText(graphics,line.Text.Substring(0,caretCharOffset),Font,new Size(),TextFormatFlags).Width;
else
return TextRenderer.MeasureText(graphics,line.Text,Font,new Size(),TextFormatFlags).Width;
}
public override void Print(CodeEnginePrintDocument document,PrintPageEventArgs e)
{
if(document["page"]==null)
{
//Haven't done any printing yet.
document["page"]=1;
document["line"]=1;
document["char"]=1;
}
}
public override string ToString()
{
return "Default Text";
}

internal bool ShouldSerializeFont()
{
return !Font.Equals(Control.DefaultFont);
}
public new Font Font
{
get{return base.Font;}
set{base.Font=value;}
}
[DefaultValue(typeof(Color),"ControlLightLight")]
public Color BackgroundColor
{
get{return BackColor;}
set{BackColor=value;}
}
[DefaultValue(typeof(Color),"ControlText")]
public Color TextColor
{
get{return ForeColor;}
set{ForeColor=value;}
}
}


I'll likely wait to explain everything properly when I get around to writing the documentation for making one's own CodeEngine, which will be after I write up some Intellisense-type stuff, if I even bother with that. Also, the Print function is a bit lacking since I'm, again, having problems with that.

Please feel free to comment, critique, question, etc.

Mirrored here


[Edit: Fixed my not having swapped pre tags for source]
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now