diff --git a/minigui.d b/minigui.d index 2c45602..37cbb48 100644 --- a/minigui.d +++ b/minigui.d @@ -1835,6 +1835,17 @@ class ScrollableWidget : Widget { horizontalScrollBar = new HorizontalScrollbar(horizontalScrollbarHolder); verticalScrollBar = new VerticalScrollbar(verticalScrollbarHolder); + // TOTAL HACKS + horizontalScrollBar.addEventListener("mousedown", (Event event) { event.preventDefault(); }); + verticalScrollBar.addEventListener("mousedown", (Event event) { event.preventDefault(); }); + horizontalScrollBar.addEventListener("mousemove", (Event event) { event.preventDefault(); }); + verticalScrollBar.addEventListener("mousemove", (Event event) { event.preventDefault(); }); + + /* + clientAreaHolder = new FixedPosition(this); + clientArea = new ScrollableClientWidget(clientAreaHolder); + */ + horizontalScrollbarHolder.showing_ = false; verticalScrollbarHolder.showing_ = false; @@ -1894,6 +1905,11 @@ class ScrollableWidget : Widget { VerticalScrollbar verticalScrollBar; HorizontalScrollbar horizontalScrollBar; + + /* + FixedPosition clientAreaHolder; + Widget clientArea; + */ } version(custom_widgets) @@ -1910,6 +1926,13 @@ class ScrollableWidget : Widget { verticalScrollbarHolder.x = this.width - verticalScrollBar.minWidth(); verticalScrollbarHolder.y = 0 + 2; + /* + clientAreaHolder.x = 0; + clientAreaHolder.y = 0; + clientAreaHolder.width = this.width - verticalScrollBar.width; + clientAreaHolder.height = this.height - horizontalScrollBar.height; + */ + if(contentWidth_ <= this.width) scrollOrigin_.x = 0; if(contentHeight_ <= this.height) @@ -2182,7 +2205,7 @@ class ScrollableWidget : Widget { painter.originX = painter.originX - scrollOrigin.x; painter.originY = painter.originY - scrollOrigin.y; if(force || redrawRequested) { - painter.setClipRectangle(scrollOrigin, viewportWidth(), viewportHeight()); + painter.setClipRectangle(scrollOrigin + Point(2, 2) /* border */, viewportWidth() - 4 /* border */, viewportHeight() - 4 /* border */); //erase(painter); // we paintFrameAndBackground above so no need paint(painter); @@ -2199,6 +2222,17 @@ class ScrollableWidget : Widget { } } +/* +class ScrollableClientWidget : Widget { + this(Widget parent) { + super(parent); + } + override void paint(ScreenPainter p) { + parent.paint(p); + } +} +*/ + /// abstract class ScrollbarBase : Widget { /// @@ -5434,6 +5468,12 @@ abstract class EditableTextWidget : EditableTextWidgetParent { version(custom_widgets) override void paintFrameAndBackground(ScreenPainter painter) { this.draw3dFrame(painter, FrameStyle.sunk, Color.white); + if(horizontalScrollbarHolder.showing && verticalScrollbarHolder.showing) { + // just paint over the lower-right corner + painter.outlineColor = windowBackgroundColor; + painter.fillColor = windowBackgroundColor; + painter.drawRectangle(Point(width - 16, height - 16), Size(16, 16)); + } } version(win32_widgets) { /* will do it with Windows calls in the classes */ } @@ -5480,9 +5520,11 @@ abstract class EditableTextWidget : EditableTextWidgetParent { super.defaultEventHandler_mousedown(ev); if(parentWindow.win.closed) return; if(ev.button == MouseButton.left) { + if(textLayout.selectNone()) + redraw(); textLayout.moveCaretToPixelCoordinates(ev.clientX, ev.clientY); this.focus(); - this.parentWindow.win.grabInput(); + //this.parentWindow.win.grabInput(); } else if(ev.button == MouseButton.middle) { static if(UsingSimpledisplayX11) { getPrimarySelection(parentWindow.win, (txt) { @@ -5498,7 +5540,7 @@ abstract class EditableTextWidget : EditableTextWidgetParent { version(custom_widgets) override void defaultEventHandler_mouseup(Event ev) { - this.parentWindow.win.releaseInputGrab(); + //this.parentWindow.win.releaseInputGrab(); super.defaultEventHandler_mouseup(ev); } diff --git a/simpledisplay.d b/simpledisplay.d index d39f3e8..3c428c4 100644 --- a/simpledisplay.d +++ b/simpledisplay.d @@ -6343,6 +6343,15 @@ struct ScreenPainter { impl.drawRectangle(upperLeft.x, upperLeft.y, width, height); } + /// ditto + void drawRectangle(Point upperLeft, Size size) { + if(impl is null) return; + if(isClipped(upperLeft, size.width, size.height)) return; + transform(upperLeft); + impl.drawRectangle(upperLeft.x, upperLeft.y, size.width, size.height); + } + + /// ditto void drawRectangle(Point upperLeft, Point lowerRightInclusive) { if(impl is null) return; if(isClipped(upperLeft, lowerRightInclusive + Point(1, 1))) return; @@ -8455,6 +8464,7 @@ version(X11) { /// for cross-platform compatibility. //__gshared string xfontstr = "-*-dejavu sans-medium-r-*-*-12-*-*-*-*-*-*-*"; __gshared string xfontstr = "-*-lucida-medium-r-normal-sans-12-*-*-*-*-*-*-*"; + //__gshared string xfontstr = "-*-fixed-medium-r-*-*-14-*-*-*-*-*-*-*"; alias int delegate(XEvent) NativeEventHandler; alias Window NativeWindowHandle; @@ -13014,229 +13024,17 @@ version(X11) { } mixin template ExperimentalTextComponent2() { - - enum TextFormat : ushort { - // decorations - underline = 1, - strikethrough = 2, - - // font selectors - - bold = 0x4000 | 1, // weight 700 - light = 0x4000 | 2, // weight 300 - veryBoldOrLight = 0x4000 | 4, // weight 100 with light, weight 900 with bold - // bold | light is really invalid but should give weight 500 - // veryBoldOrLight without one of the others should just give the default for the font; it should be ignored. - - italic = 0x4000 | 8, - smallcaps = 0x4000 | 16, - } - - - struct Decoration { - ushort id; - Color foreground; - Color background; - ushort textFormat; - void* font; - } - - Decoration[] decorations; - - struct TextState { - char[] text; - int[] x; - int[] y; - ushort[] decorationId; - int length; - - int caret; - - void makeGap(int where, int minLength) { - int gapSize = 0; - int at = where; - while(at < text.length && text[at] == 0xff) { - at++; - gapSize++; - } - - if(gapSize >= minLength) - return; - - // try to gather gap from behind us, if any - /* - at = where - 32; - if(at < 0) - at = 0; - - while(at < where - 1) { - if(text[at] == 0xff) { - text[at] = text[at + 1]; - x[at] = x[at + 1]; - y[at] = y[at + 1]; - decorationId[at] = decorationId[at + 1]; - text[at + 1] = 0xff; - gapSize++; - } - at++; - } - - if(gapSize >= minLength) - return; - */ - keep_trying: - at = where; - while(at + 1 < text.length) { - // FIXME it needs to work on a whole block, not just one char - if(text[at + 1] == 0xff) { - text[at + 1] = text[at]; - x[at + 1] = x[at]; - y[at + 1] = y[at]; - decorationId[at + 1] = decorationId[at]; - text[at] = 0xff; - gapSize++; - if(gapSize >= minLength) - return; - } - at++; - } - - if(gapSize < minLength) { - auto increase = 16; - if(minLength - gapSize > 16) - increase = minLength - gapSize; - text.length += increase; - x.length += increase; - y.length += increase; - decorationId.length += increase; - text[$ - increase .. $] = 0xff; - goto keep_trying; - } - } - - void insert(dchar c) { - makeGap(caret, 1); - text[caret] = cast(char) c; - caret++; - length++; - layout(caret - 1, cast(int) text.length, false); - } - - string toPlainText() { - string s; - s.reserve(length); - foreach(char ch; text) - if(ch != 0xff) - s ~= ch; - return s; - } - - void resetContents(in char[] to) { - if(text.length < to.length * 2) { - text.length = to.length * 2; - x.length = text.length; - y.length = text.length; - decorationId.length = text.length; - } - int textPos = 0; - int skipped = 0; - foreach(ch; to) { - if(ch == 13) { - ++skipped; - continue; - } - text[textPos++] = ch; - text[textPos++] = 0xff; - } - text[textPos .. $] = 0xff; - length = cast(int) to.length - skipped; - - decorationId[0 .. length] = 0; - - layout(0, text.length, true); - } - - int lineHeight = 14; - int letterWidth = 7; - int tabStop = 4; - - void layout(int start, int end, bool forceAll) { - int x = 0, y = 0; - foreach(idx, char ch; text[start .. end]) { - if(ch == 0xff) - continue; - if(!forceAll && this.x[start + idx] == x && this.y[start + idx] == y) - break; // seems to already be done! - this.x[start + idx] = x; - this.y[start + idx] = y; - - // FIXME unicode - - if(ch == '\n') { - x = 0; - y += lineHeight; - } else if(ch == '\t') { - x += x % (letterWidth * tabStop); - } else { - x += letterWidth; - } - } - } - - void drawInto(ScreenPainter painter, int dx, int dy, int sx, int sy, int width, int height) { - //char[6] buffer; - // FIXME unicode - - painter.outlineColor = Color.white; - painter.fillColor = Color.white; - painter.drawRectangle(Point(dx, dy), width, height); - - painter.outlineColor = Color.black; - - if(length == 0) - return; - - int startingIdx = 0; - if(sx > 0 || sy > 0) { - int lastSearched = text.length; - // binary search till we get the first visible item - startingIdx = text.length / 2; - keep_searching: - while(startingIdx >= 0 && text[startingIdx] == 0xff) - --startingIdx; - while(text[startingIdx] == 0xff && startingIdx < text.length) - ++startingIdx; - if(startingIdx == text.length) - assert(0); // we're apparently empty! why didn't length == 0? - - if(this.x[startingIdx] > sx || this.y[startingIdx] > sy) { - // FIXME - // too far ahead, search backward - lastSearched = startingIdx; - startingIdx = startingIdx / 2; - goto keep_searching; - } else { - // this is probably good enough but let's try to be more precise - //startingIdx = (lastSearched - startingIdx) / 2; - //goto keep_searching; - } - } - - foreach(idx, char ch; text[startingIdx .. $]) { - if(ch == 0xff) - continue; - int drawX = dx + this.x[startingIdx + idx] - sx; - int drawY = dy + this.y[startingIdx + idx] - sy; - - if(drawX - dx > width) - continue; - if(drawY - dy > height) - break; - - painter.drawText(Point(drawX, drawY), "" ~ ch); - } - } - } + /+ + Stage 1: get it working monospace + Stage 2: use proportional font + Stage 3: allow changes in inline style + Stage 4: allow new fonts and sizes in the middle + Stage 5: optimize gap buffer + Stage 6: optimize layout + Stage 7: word wrap + Stage 8: justification + Stage 9: editing, selection, etc. + +/ } @@ -14097,8 +13895,12 @@ mixin template ExperimentalTextComponent() { moveDocumentStart(selectionStart); moveDocumentEnd(selectionEnd); } - void selectNone() { - selectionStart = selectionEnd = Caret.init; + bool selectNone() { + if(selectionStart != selectionEnd) { + selectionStart = selectionEnd = Caret.init; + return true; + } + return false; } /// Rich text editing api. These allow you to manipulate the meta data of the current element and add new elements.