Added help popup, Ctrl-Left and Right cursor movement by word
authorChip Black <bytex64@bytex64.net>
Tue, 30 Sep 2008 00:40:51 +0000 (19:40 -0500)
committerChip Black <bytex64@bytex64.net>
Tue, 30 Sep 2008 00:40:51 +0000 (19:40 -0500)
HelpBox.cs [new file with mode: 0644]
Makefile
TextDisplay.cs
TextDocument.cs
control_help [new file with mode: 0644]
main.cs

diff --git a/HelpBox.cs b/HelpBox.cs
new file mode 100644 (file)
index 0000000..664d5d9
--- /dev/null
@@ -0,0 +1,123 @@
+using System.Collections;
+using System.IO;
+using System;
+using Cairo;
+using Pango;
+
+public struct HelpItem {
+       public string Key;
+       public string Text;
+
+       public HelpItem(string key, string text) {
+               Key = key;
+               Text = text;
+       }
+
+       public override string ToString() {
+               return "<b>" + Key + "</b>\t" + Text;
+       }
+}
+
+public class HelpBox {
+       private Pango.Context pc;
+       private Pango.FontDescription titleFont;
+       private Pango.FontDescription textFont;
+       private string helpTitle;
+       private HelpItem[] helpText;
+       private int Width = 480;
+       private int Height = 360;
+       private int Columns = 3;
+
+       public HelpBox(Pango.Context pc, string title, HelpItem[] items) {
+               this.pc = pc;
+               helpTitle = title;
+               helpText = items;
+               init();
+       }
+
+       public HelpBox(Pango.Context pc, string filename) {
+               StreamReader file = new StreamReader(filename);
+               string line;
+               ArrayList items = new ArrayList();
+
+               this.pc = pc;
+               helpTitle = file.ReadLine();
+               while ((line = file.ReadLine()) != null) {
+                       string[] i = line.Split(null);
+                       if (i.Length == 2) {
+                               items.Add(new HelpItem(i[0], i[1]));
+                       }
+               }
+               helpText = new HelpItem[items.Count];
+               for (int i = 0; i < items.Count; i++) {
+                       helpText[i] = (HelpItem) items[i];
+               }
+               init();
+       }
+
+       private void init() {
+               titleFont = Pango.FontDescription.FromString("Helvetica Bold 24");
+               textFont = Pango.FontDescription.FromString("Helvetica 16");
+               Pango.FontMetrics fmTitle = pc.GetMetrics(titleFont, null);
+               Pango.FontMetrics fmText = pc.GetMetrics(textFont, null);
+               int hTitle = Pango.Units.ToPixels(fmTitle.Ascent + fmTitle.Descent);
+               int hText = Pango.Units.ToPixels(fmText.Ascent + fmText.Descent);
+
+               Width = 480;
+               for (int i = 0; i < helpText.Length; i++) {
+                       Pango.Layout layout = new Pango.Layout(pc);
+                       Pango.Rectangle r, r2;
+
+                       layout.FontDescription = textFont;
+                       layout.SetMarkup(helpText[i].ToString());
+                       layout.GetExtents(out r, out r2);
+                       int pw = Pango.Units.ToPixels(r2.Width);
+                       if ((pw + 8) * Columns > Width) {
+                               Width = (pw + 8) * Columns;
+                       }
+               }
+               Height = hTitle + (helpText.Length / 3 + 1) * hText + 12;
+       }
+
+        public void draw(Cairo.Context gr, int width, int height) {
+               Pango.Layout layout;
+               Cairo.Rectangle r = new Cairo.Rectangle(
+                       width / 2 - Width / 2,
+                       height / 2 - Height / 2,
+                       Width, Height);
+
+               gr.SetSourceRGBA(1.0, 1.0, 1.0, 0.8);
+               gr.NewPath();
+               gr.Rectangle(r);
+               gr.Fill();
+
+               gr.SetSourceRGB(0.0, 0.0, 0.0);
+               gr.NewPath();
+               gr.Rectangle(r);
+               gr.Stroke();
+
+               gr.MoveTo(r.X + 4, r.Y + 4);
+               layout = new Pango.Layout(pc);
+               layout.SetText(helpTitle);
+               layout.Width = Pango.Units.FromPixels((int)r.Width);
+               layout.FontDescription = titleFont;
+               Pango.CairoHelper.ShowLayout(gr, layout);
+
+               int ii = 0;
+               int ColumnWidth = Width / Columns;
+               int ColumnLines = helpText.Length / Columns + 1;
+               for (int i = 0; i < Columns; i++) {
+                       gr.MoveTo(r.X + 4 + (i * ColumnWidth), r.Y + 38);
+                       string column = "";
+                       for (int j = 0; j < ColumnLines && ii < helpText.Length; j++) {
+                               column += helpText[ii].ToString() + "\n";
+                               ii++;
+                       }
+                       layout = new Pango.Layout(pc);
+                       layout.SetMarkup(column);
+                       layout.Width = Pango.Units.FromPixels(ColumnWidth);
+                       layout.FontDescription = textFont;
+                       Pango.CairoHelper.ShowLayout(gr, layout);
+               }
+        }
+}
index 9e3e810..d4289da 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 CSFLAGS = -debug -pkg:gtk-sharp-2.0 -r:Mono.Cairo
 SOURCES = main.cs Interfaces.cs TextDisplay.cs TextDocument.cs TextInput.cs \
-         FileInput.cs
+         FileInput.cs HelpBox.cs
 EXE = Nebula.exe
 
 all: $(EXE)
index 4e753de..282fc7f 100644 (file)
@@ -1,22 +1,30 @@
 using System;
+using System.Timers;
 using Cairo;
 using Pango;
+using Gdk;
 using Gtk;
 
 public class TextDisplay : DrawingArea, IKeyPress {
        private TextDocument doc = null;
-       private Font font;
+       private Pango.Font font;
        // FIXME: Proportional fonts?
-       private float fontwidth;
-       private float fontheight;
+       private float fontWidth;
+       private float fontHeight;
+
+       private Timer helpTimer;
+       private HelpBox controlHelp;
+       private bool showHelp;
 
        public TextDisplay() {
                FontDescription fd = Pango.FontDescription.FromString("Courier 12");
                font = this.PangoContext.LoadFont(fd);
                FontMetrics fm = font.GetMetrics(null);
-               fontheight = Pango.Units.ToPixels(fm.Ascent + fm.Descent);
-               fontwidth = fontheight * 0.5333f;
-               Console.WriteLine("Using {0} {1}pt ({2}x{3}px)", fd.Family, fd.Size, fontwidth, fontheight);
+               fontHeight = Pango.Units.ToPixels(fm.Ascent + fm.Descent);
+               fontWidth = fontHeight * 0.5333f;
+               Console.WriteLine("Using {0} {1}pt ({2}x{3}px)", fd.Family, fd.Size, fontWidth, fontHeight);
+
+               controlHelp = new HelpBox(this.PangoContext, "control_help");
                ModifyBg(StateType.Normal, new Gdk.Color(0xFF, 0xFF, 0xFF));
        }
 
@@ -37,14 +45,17 @@ public class TextDisplay : DrawingArea, IKeyPress {
                gr.MoveTo(0, 0);
                Pango.CairoHelper.ShowLayout(gr, layout);
 
-               gr.Operator = Operator.Xor;
+               gr.SetSourceRGB(0.0, 0.0, 0.0);
                gr.NewPath();
-               gr.Rectangle((doc.cursor.x - doc.textorigin.x)*fontwidth, (doc.cursor.y - doc.textorigin.y)*fontheight, fontwidth, fontheight);
+               gr.Rectangle((doc.cursor.x - doc.textorigin.x)*fontWidth, (doc.cursor.y - doc.textorigin.y)*fontHeight, fontWidth, fontHeight);
                gr.Fill();
+
+               if (showHelp) {
+                       controlHelp.draw(gr, width, height);
+               }
         }
 
-        protected override bool OnExposeEvent(Gdk.EventExpose args)
-        {
+        protected override bool OnExposeEvent(Gdk.EventExpose args) {
                 Gdk.Window win = args.Window;                
 
                #if OLD_SYSTEMS
@@ -74,17 +85,61 @@ public class TextDisplay : DrawingArea, IKeyPress {
        protected override void OnSizeAllocated(Gdk.Rectangle r) {
                base.OnSizeAllocated(r);
                if (doc != null)
-                       doc.SetPageSize((int)(r.Width / fontwidth) - 1, (int)(r.Height / fontheight) - 1);
+                       doc.SetPageSize((int)(r.Width / fontWidth) - 1, (int)(r.Height / fontHeight) - 1);
        }
 
        public void Update() {
                QueueDraw();
        }
 
+       public void ShowHelp() {
+               showHelp = true;
+               helpTimer = null;
+               QueueDraw();
+       }
+
+       public void HideHelp() {
+               if (helpTimer != null) {
+                       helpTimer.Stop();
+                       helpTimer = null;
+               }
+               if (showHelp)
+                       showHelp = false;
+               QueueDraw();
+       }
 
         [GLib.ConnectBefore ()]
         public void KeyPress(object o, KeyPressEventArgs args) {
                if (doc == null) return;
+               if ((args.Event.State & ModifierType.ControlMask) != 0) {
+                       switch(args.Event.Key) {
+                       default:
+                               HideHelp();
+                               break;
+                       }
+               } else {
+                       switch(args.Event.Key) {
+                       case Gdk.Key.Control_L:
+                       case Gdk.Key.Control_R:
+                               helpTimer = new Timer();
+                               helpTimer.Elapsed += delegate(object o2, ElapsedEventArgs e) {
+                                       ShowHelp();
+                               };
+                               helpTimer.AutoReset = false;
+                               helpTimer.Interval = 1000;
+                               helpTimer.Start();
+                               break;
+                       }
+               }
                doc.KeyPress(o, args);
        }
+
+       public void KeyRelease(object o, KeyReleaseEventArgs e) {
+               switch(e.Event.Key) {
+               case Gdk.Key.Control_L:
+               case Gdk.Key.Control_R:
+                       HideHelp();
+                       break;
+               }
+       }
 }
index 4f1103f..21f813a 100644 (file)
@@ -151,9 +151,13 @@ public class TextDocument : IKeyPress {
        public string GetTextWindow() {
                string s = "";
 
-               for (int i = textorigin.y; textorigin.y + i < text.Lines.Count && i < pageheight; i++) {
-                       string l = (string)text.Lines[i];
-                       s += l.Substring(textorigin.x) + "\n";
+               for (int i = 0; i <= pageheight + 1 && textorigin.y + i < text.Lines.Count; i++) {
+                       string l = (string)text.Lines[textorigin.y + i];
+                       if (textorigin.x > l.Length) {
+                               s += "\n";
+                       } else {
+                               s += l.Substring(textorigin.x) + "\n";
+                       }
                }
                return s;
        }
@@ -198,12 +202,72 @@ public class TextDocument : IKeyPress {
                        cursor.y += pageheight;
                        break;
                }
-               FixTextWindow();
+       }
+
+       public void NextWord() {
+               if (cursor.x >= text.LineLength(cursor.y)) {
+                       cursor.y++;
+                       cursor.x = 0;
+               } else {
+                       string s = (String)text.Lines[cursor.y];
+                       int i = s.IndexOf(' ', cursor.x);
+                       if (i == -1) {
+                               cursor.y++;
+                               cursor.x = 0;
+                       } else {
+                               cursor.x = i + 1;
+                       }
+               }
+       }
+
+       public void PreviousWord() {
+               if (cursor.y > text.Lines.Count) {
+                       cursor.x = 0;
+                       cursor.y--;
+               } else if (cursor.y == text.Lines.Count) {
+                       string s = (String)text.Lines[text.Lines.Count - 1];
+                       int i = s.LastIndexOf(' ');
+                       if (i == -1) {
+                               cursor.x = 0;
+                       } else {
+                               cursor.x = i + 1;
+                       }
+                       cursor.y--;
+               } else if (cursor.x == 0 && cursor.y > 0) {
+                       string s = (String)text.Lines[cursor.y - 1];
+                       int i = s.LastIndexOf(' ');
+                       if (i == -1) {
+                               cursor.x = 0;
+                       } else {
+                               cursor.x = i + 1;
+                       }
+                       cursor.y--;
+               } else if (cursor.x == 0 && cursor.y == 0) {
+                       return;
+               } else {
+                       string s = (String)text.Lines[cursor.y];
+                       if (cursor.x >= s.Length)
+                               cursor.x = s.Length - 1;
+                       int i = s.LastIndexOf(' ', cursor.x - 2, cursor.x - 2);
+                       if (i == -1) {
+                               cursor.x = 0;
+                       } else {
+                               cursor.x = i + 1;
+                       }
+               }
        }
 
        [GLib.ConnectBefore ()]
        public void KeyPress(object o, Gtk.KeyPressEventArgs args) {
                if ((args.Event.State & Gdk.ModifierType.ControlMask) != 0) {
+                       switch(args.Event.Key) {
+                       case Gdk.Key.Right:
+                               NextWord();
+                               break;
+                       case Gdk.Key.Left:
+                               PreviousWord();
+                               break;
+                       }
                } else if ((args.Event.State & Gdk.ModifierType.Mod1Mask) != 0) {
                } else {
                        switch(args.Event.Key) {
@@ -242,6 +306,7 @@ public class TextDocument : IKeyPress {
                                break;
                        }
                }
+               FixTextWindow();
        }
        
        public void AddChar(int c) {
diff --git a/control_help b/control_help
new file mode 100644 (file)
index 0000000..3999d55
--- /dev/null
@@ -0,0 +1,7 @@
+Control Hotkeys
+N      New
+O      Open
+S      Save
+Q      Quit
+->     Next Word
+<-     Previous Word
diff --git a/main.cs b/main.cs
index f9570c5..1978b5f 100644 (file)
--- a/main.cs
+++ b/main.cs
@@ -31,6 +31,7 @@ public class Nebula {
                 window.ShowAll();
 
                window.KeyPressEvent += new KeyPressEventHandler(KeyPress);
+               window.KeyReleaseEvent += display.KeyRelease;
                SelectInput(display);
                window.DeleteEvent += delegate(object o, DeleteEventArgs e) {
                        Application.Quit();
@@ -65,7 +66,7 @@ public class Nebula {
                                        }
                                        SelectInput(display);
                                        stack.Remove(input);
-                                       input.Destroy();
+                                       input = null;
                                };
                                break;
                        case Gdk.Key.s: