From c87b4593cee7ebd588e0fe8541a3067061d02cb0 Mon Sep 17 00:00:00 2001 From: Chip Black Date: Mon, 29 Sep 2008 19:40:51 -0500 Subject: [PATCH] Added help popup, Ctrl-Left and Right cursor movement by word --- HelpBox.cs | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ Makefile | 2 +- TextDisplay.cs | 77 +++++++++++++++++++++++++----- TextDocument.cs | 73 ++++++++++++++++++++++++++-- control_help | 7 +++ main.cs | 3 +- 6 files changed, 268 insertions(+), 17 deletions(-) create mode 100644 HelpBox.cs create mode 100644 control_help diff --git a/HelpBox.cs b/HelpBox.cs new file mode 100644 index 0000000..664d5d9 --- /dev/null +++ b/HelpBox.cs @@ -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 "" + Key + "\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); + } + } +} diff --git a/Makefile b/Makefile index 9e3e810..d4289da 100644 --- 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) diff --git a/TextDisplay.cs b/TextDisplay.cs index 4e753de..282fc7f 100644 --- a/TextDisplay.cs +++ b/TextDisplay.cs @@ -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; + } + } } diff --git a/TextDocument.cs b/TextDocument.cs index 4f1103f..21f813a 100644 --- a/TextDocument.cs +++ b/TextDocument.cs @@ -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 index 0000000..3999d55 --- /dev/null +++ b/control_help @@ -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 --- 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: -- 2.25.1