mirror of https://github.com/adamdruppe/arsd.git
merge
This commit is contained in:
commit
1d14657596
|
@ -18,22 +18,26 @@
|
|||
ought to just work.
|
||||
|
||||
Example:
|
||||
---
|
||||
auto data = cast(immutable(ubyte)[])
|
||||
std.file.read("my-windows-file.txt");
|
||||
string utf8String = convertToUtf8(data, "windows-1252");
|
||||
// utf8String can now be used
|
||||
---
|
||||
|
||||
|
||||
The encodings currently implemented for decoding are:
|
||||
UTF-8 (a no-op; it simply casts the array to string)
|
||||
UTF-16,
|
||||
UTF-32,
|
||||
Windows-1252,
|
||||
ISO 8859 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, and 16.
|
||||
$(LIST
|
||||
* UTF-8 (a no-op; it simply casts the array to string)
|
||||
* UTF-16,
|
||||
* UTF-32,
|
||||
* Windows-1252,
|
||||
* ISO 8859 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, and 16.
|
||||
* KOI8-R
|
||||
)
|
||||
|
||||
It treats ISO 8859-1, Latin-1, and Windows-1252 the same way, since
|
||||
those labels are pretty much de-facto the same thing in wild documents.
|
||||
|
||||
those labels are pretty much de-facto the same thing in wild documents (people mislabel them a lot and I found it more useful to just deal with it than to be pedantic).
|
||||
|
||||
This module currently makes no attempt to look at control characters.
|
||||
*/
|
||||
|
|
182
color.d
182
color.d
|
@ -6,8 +6,8 @@ module arsd.color;
|
|||
// importing phobos explodes the size of this code 10x, so not doing it.
|
||||
|
||||
private {
|
||||
real toInternal(T)(scope const(char)[] s) {
|
||||
real accumulator = 0.0;
|
||||
double toInternal(T)(scope const(char)[] s) {
|
||||
double accumulator = 0.0;
|
||||
size_t i = s.length;
|
||||
foreach(idx, c; s) {
|
||||
if(c >= '0' && c <= '9') {
|
||||
|
@ -17,21 +17,21 @@ private {
|
|||
i = idx + 1;
|
||||
break;
|
||||
} else {
|
||||
string wtfIsWrongWithThisStupidLanguageWithItsBrokenSafeAttribute = "bad char to make real from ";
|
||||
string wtfIsWrongWithThisStupidLanguageWithItsBrokenSafeAttribute = "bad char to make double from ";
|
||||
wtfIsWrongWithThisStupidLanguageWithItsBrokenSafeAttribute ~= s;
|
||||
throw new Exception(wtfIsWrongWithThisStupidLanguageWithItsBrokenSafeAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
real accumulator2 = 0.0;
|
||||
real count = 1;
|
||||
double accumulator2 = 0.0;
|
||||
double count = 1;
|
||||
foreach(c; s[i .. $]) {
|
||||
if(c >= '0' && c <= '9') {
|
||||
accumulator2 *= 10;
|
||||
accumulator2 += c - '0';
|
||||
count *= 10;
|
||||
} else {
|
||||
string wtfIsWrongWithThisStupidLanguageWithItsBrokenSafeAttribute = "bad char to make real from ";
|
||||
string wtfIsWrongWithThisStupidLanguageWithItsBrokenSafeAttribute = "bad char to make double from ";
|
||||
wtfIsWrongWithThisStupidLanguageWithItsBrokenSafeAttribute ~= s;
|
||||
throw new Exception(wtfIsWrongWithThisStupidLanguageWithItsBrokenSafeAttribute);
|
||||
}
|
||||
|
@ -64,8 +64,8 @@ private {
|
|||
|
||||
return cast(string) ret;
|
||||
}
|
||||
string toInternal(T)(real a) {
|
||||
// a simplifying assumption here is the fact that we only use this in one place: toInternal!string(cast(real) a / 255)
|
||||
string toInternal(T)(double a) {
|
||||
// a simplifying assumption here is the fact that we only use this in one place: toInternal!string(cast(double) a / 255)
|
||||
// thus we know this will always be between 0.0 and 1.0, inclusive.
|
||||
if(a <= 0.0)
|
||||
return "0.0";
|
||||
|
@ -78,16 +78,16 @@ private {
|
|||
}
|
||||
|
||||
nothrow @safe @nogc pure
|
||||
real absInternal(real a) { return a < 0 ? -a : a; }
|
||||
double absInternal(double a) { return a < 0 ? -a : a; }
|
||||
nothrow @safe @nogc pure
|
||||
real minInternal(real a, real b, real c) {
|
||||
double minInternal(double a, double b, double c) {
|
||||
auto m = a;
|
||||
if(b < m) m = b;
|
||||
if(c < m) m = c;
|
||||
return m;
|
||||
}
|
||||
nothrow @safe @nogc pure
|
||||
real maxInternal(real a, real b, real c) {
|
||||
double maxInternal(double a, double b, double c) {
|
||||
auto m = a;
|
||||
if(b > m) m = b;
|
||||
if(c > m) m = c;
|
||||
|
@ -217,6 +217,7 @@ struct Color {
|
|||
|
||||
/// Return black-and-white color
|
||||
Color toBW() () nothrow pure @safe @nogc {
|
||||
// FIXME: gamma?
|
||||
int intens = clampToByte(cast(int)(0.2126*r+0.7152*g+0.0722*b));
|
||||
return Color(intens, intens, intens, a);
|
||||
}
|
||||
|
@ -226,7 +227,7 @@ struct Color {
|
|||
if(a == 255)
|
||||
return "#" ~ toHexInternal(r) ~ toHexInternal(g) ~ toHexInternal(b);
|
||||
else {
|
||||
return "rgba("~toInternal!string(r)~", "~toInternal!string(g)~", "~toInternal!string(b)~", "~toInternal!string(cast(real)a / 255.0)~")";
|
||||
return "rgba("~toInternal!string(r)~", "~toInternal!string(g)~", "~toInternal!string(b)~", "~toInternal!string(cast(double)a / 255.0)~")";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,15 +278,15 @@ struct Color {
|
|||
assert(s[$-1] == ')');
|
||||
s = s[s.startsWithInternal("hsl(") ? 4 : 5 .. $ - 1]; // the closing paren
|
||||
|
||||
real[3] hsl;
|
||||
double[3] hsl;
|
||||
ubyte a = 255;
|
||||
|
||||
auto parts = s.splitInternal(',');
|
||||
foreach(i, part; parts) {
|
||||
if(i < 3)
|
||||
hsl[i] = toInternal!real(part.stripInternal);
|
||||
hsl[i] = toInternal!double(part.stripInternal);
|
||||
else
|
||||
a = clampToByte(cast(int) (toInternal!real(part.stripInternal) * 255));
|
||||
a = clampToByte(cast(int) (toInternal!double(part.stripInternal) * 255));
|
||||
}
|
||||
|
||||
c = .fromHsl(hsl);
|
||||
|
@ -302,7 +303,7 @@ struct Color {
|
|||
auto parts = s.splitInternal(',');
|
||||
foreach(i, part; parts) {
|
||||
// lol the loop-switch pattern
|
||||
auto v = toInternal!real(part.stripInternal);
|
||||
auto v = toInternal!double(part.stripInternal);
|
||||
switch(i) {
|
||||
case 0: // red
|
||||
c.r = clampToByte(cast(int) v);
|
||||
|
@ -353,7 +354,7 @@ struct Color {
|
|||
}
|
||||
|
||||
/// from hsl
|
||||
static Color fromHsl(real h, real s, real l) {
|
||||
static Color fromHsl(double h, double s, double l) {
|
||||
return .fromHsl(h, s, l);
|
||||
}
|
||||
|
||||
|
@ -460,22 +461,26 @@ private ubyte fromHexInternal(in char[] s) {
|
|||
|
||||
/// Converts hsl to rgb
|
||||
Color fromHsl(real[3] hsl) nothrow pure @safe @nogc {
|
||||
return fromHsl(cast(double) hsl[0], cast(double) hsl[1], cast(double) hsl[2]);
|
||||
}
|
||||
|
||||
Color fromHsl(double[3] hsl) nothrow pure @safe @nogc {
|
||||
return fromHsl(hsl[0], hsl[1], hsl[2]);
|
||||
}
|
||||
|
||||
/// Converts hsl to rgb
|
||||
Color fromHsl(real h, real s, real l, real a = 255) nothrow pure @safe @nogc {
|
||||
Color fromHsl(double h, double s, double l, double a = 255) nothrow pure @safe @nogc {
|
||||
h = h % 360;
|
||||
|
||||
real C = (1 - absInternal(2 * l - 1)) * s;
|
||||
double C = (1 - absInternal(2 * l - 1)) * s;
|
||||
|
||||
real hPrime = h / 60;
|
||||
double hPrime = h / 60;
|
||||
|
||||
real X = C * (1 - absInternal(hPrime % 2 - 1));
|
||||
double X = C * (1 - absInternal(hPrime % 2 - 1));
|
||||
|
||||
real r, g, b;
|
||||
double r, g, b;
|
||||
|
||||
if(h is real.nan)
|
||||
if(h is double.nan)
|
||||
r = g = b = 0;
|
||||
else if (hPrime >= 0 && hPrime < 1) {
|
||||
r = C;
|
||||
|
@ -503,7 +508,7 @@ Color fromHsl(real h, real s, real l, real a = 255) nothrow pure @safe @nogc {
|
|||
b = X;
|
||||
}
|
||||
|
||||
real m = l - C / 2;
|
||||
double m = l - C / 2;
|
||||
|
||||
r += m;
|
||||
g += m;
|
||||
|
@ -516,23 +521,33 @@ Color fromHsl(real h, real s, real l, real a = 255) nothrow pure @safe @nogc {
|
|||
cast(int)(a));
|
||||
}
|
||||
|
||||
/// Assumes the input `u` is already between 0 and 1 fyi.
|
||||
nothrow pure @safe @nogc
|
||||
double srgbToLinearRgb(double u) {
|
||||
if(u < 0.4045)
|
||||
return u / 12.92;
|
||||
else
|
||||
return ((u + 0.055) / 1.055) ^^ 2.4;
|
||||
}
|
||||
|
||||
/// Converts an RGB color into an HSL triplet. useWeightedLightness will try to get a better value for luminosity for the human eye, which is more sensitive to green than red and more to red than blue. If it is false, it just does average of the rgb.
|
||||
real[3] toHsl(Color c, bool useWeightedLightness = false) nothrow pure @trusted @nogc {
|
||||
real r1 = cast(real) c.r / 255;
|
||||
real g1 = cast(real) c.g / 255;
|
||||
real b1 = cast(real) c.b / 255;
|
||||
double[3] toHsl(Color c, bool useWeightedLightness = false) nothrow pure @trusted @nogc {
|
||||
double r1 = cast(double) c.r / 255;
|
||||
double g1 = cast(double) c.g / 255;
|
||||
double b1 = cast(double) c.b / 255;
|
||||
|
||||
real maxColor = maxInternal(r1, g1, b1);
|
||||
real minColor = minInternal(r1, g1, b1);
|
||||
double maxColor = maxInternal(r1, g1, b1);
|
||||
double minColor = minInternal(r1, g1, b1);
|
||||
|
||||
real L = (maxColor + minColor) / 2 ;
|
||||
double L = (maxColor + minColor) / 2 ;
|
||||
if(useWeightedLightness) {
|
||||
// the colors don't affect the eye equally
|
||||
// this is a little more accurate than plain HSL numbers
|
||||
L = 0.2126*r1 + 0.7152*g1 + 0.0722*b1;
|
||||
L = 0.2126*srgbToLinearRgb(r1) + 0.7152*srgbToLinearRgb(g1) + 0.0722*srgbToLinearRgb(b1);
|
||||
// maybe a better number is 299, 587, 114
|
||||
}
|
||||
real S = 0;
|
||||
real H = 0;
|
||||
double S = 0;
|
||||
double H = 0;
|
||||
if(maxColor != minColor) {
|
||||
if(L < 0.5) {
|
||||
S = (maxColor - minColor) / (maxColor + minColor);
|
||||
|
@ -557,7 +572,7 @@ real[3] toHsl(Color c, bool useWeightedLightness = false) nothrow pure @trusted
|
|||
}
|
||||
|
||||
/// .
|
||||
Color lighten(Color c, real percentage) nothrow pure @safe @nogc {
|
||||
Color lighten(Color c, double percentage) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c);
|
||||
hsl[2] *= (1 + percentage);
|
||||
if(hsl[2] > 1)
|
||||
|
@ -566,7 +581,7 @@ Color lighten(Color c, real percentage) nothrow pure @safe @nogc {
|
|||
}
|
||||
|
||||
/// .
|
||||
Color darken(Color c, real percentage) nothrow pure @safe @nogc {
|
||||
Color darken(Color c, double percentage) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c);
|
||||
hsl[2] *= (1 - percentage);
|
||||
return fromHsl(hsl);
|
||||
|
@ -574,7 +589,7 @@ Color darken(Color c, real percentage) nothrow pure @safe @nogc {
|
|||
|
||||
/// for light colors, call darken. for dark colors, call lighten.
|
||||
/// The goal: get toward center grey.
|
||||
Color moderate(Color c, real percentage) nothrow pure @safe @nogc {
|
||||
Color moderate(Color c, double percentage) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c);
|
||||
if(hsl[2] > 0.5)
|
||||
hsl[2] *= (1 - percentage);
|
||||
|
@ -590,7 +605,7 @@ Color moderate(Color c, real percentage) nothrow pure @safe @nogc {
|
|||
}
|
||||
|
||||
/// the opposite of moderate. Make darks darker and lights lighter
|
||||
Color extremify(Color c, real percentage) nothrow pure @safe @nogc {
|
||||
Color extremify(Color c, double percentage) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c, true);
|
||||
if(hsl[2] < 0.5)
|
||||
hsl[2] *= (1 - percentage);
|
||||
|
@ -626,7 +641,7 @@ Color makeTextColor(Color c) nothrow pure @safe @nogc {
|
|||
|
||||
// These provide functional access to hsl manipulation; useful if you need a delegate
|
||||
|
||||
Color setLightness(Color c, real lightness) nothrow pure @safe @nogc {
|
||||
Color setLightness(Color c, double lightness) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c);
|
||||
hsl[2] = lightness;
|
||||
return fromHsl(hsl);
|
||||
|
@ -634,28 +649,28 @@ Color setLightness(Color c, real lightness) nothrow pure @safe @nogc {
|
|||
|
||||
|
||||
///
|
||||
Color rotateHue(Color c, real degrees) nothrow pure @safe @nogc {
|
||||
Color rotateHue(Color c, double degrees) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c);
|
||||
hsl[0] += degrees;
|
||||
return fromHsl(hsl);
|
||||
}
|
||||
|
||||
///
|
||||
Color setHue(Color c, real hue) nothrow pure @safe @nogc {
|
||||
Color setHue(Color c, double hue) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c);
|
||||
hsl[0] = hue;
|
||||
return fromHsl(hsl);
|
||||
}
|
||||
|
||||
///
|
||||
Color desaturate(Color c, real percentage) nothrow pure @safe @nogc {
|
||||
Color desaturate(Color c, double percentage) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c);
|
||||
hsl[1] *= (1 - percentage);
|
||||
return fromHsl(hsl);
|
||||
}
|
||||
|
||||
///
|
||||
Color saturate(Color c, real percentage) nothrow pure @safe @nogc {
|
||||
Color saturate(Color c, double percentage) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c);
|
||||
hsl[1] *= (1 + percentage);
|
||||
if(hsl[1] > 1)
|
||||
|
@ -664,7 +679,7 @@ Color saturate(Color c, real percentage) nothrow pure @safe @nogc {
|
|||
}
|
||||
|
||||
///
|
||||
Color setSaturation(Color c, real saturation) nothrow pure @safe @nogc {
|
||||
Color setSaturation(Color c, double saturation) nothrow pure @safe @nogc {
|
||||
auto hsl = toHsl(c);
|
||||
hsl[1] = saturation;
|
||||
return fromHsl(hsl);
|
||||
|
@ -785,9 +800,9 @@ void main() {
|
|||
import browser.document;
|
||||
foreach(ele; document.querySelectorAll("input")) {
|
||||
ele.addEventListener("change", {
|
||||
auto h = toInternal!real(document.querySelector("input[name=h]").value);
|
||||
auto s = toInternal!real(document.querySelector("input[name=s]").value);
|
||||
auto l = toInternal!real(document.querySelector("input[name=l]").value);
|
||||
auto h = toInternal!double(document.querySelector("input[name=h]").value);
|
||||
auto s = toInternal!double(document.querySelector("input[name=s]").value);
|
||||
auto l = toInternal!double(document.querySelector("input[name=l]").value);
|
||||
|
||||
Color c = Color.fromHsl(h, s, l);
|
||||
|
||||
|
@ -1081,6 +1096,79 @@ class TrueColorImage : MemoryImage {
|
|||
}
|
||||
}
|
||||
|
||||
/+
|
||||
/// An RGB array of image data.
|
||||
class TrueColorImageWithoutAlpha : MemoryImage {
|
||||
struct Data {
|
||||
ubyte[] bytes; // the data as rgba bytes. Stored left to right, top to bottom, no padding.
|
||||
}
|
||||
|
||||
/// .
|
||||
Data imageData;
|
||||
|
||||
int _width;
|
||||
int _height;
|
||||
|
||||
override void clearInternal () nothrow @system {// @nogc {
|
||||
import core.memory : GC;
|
||||
// it is safe to call [GC.free] with `null` pointer.
|
||||
GC.free(imageData.bytes.ptr); imageData.bytes = null;
|
||||
_width = _height = 0;
|
||||
}
|
||||
|
||||
/// .
|
||||
override TrueColorImageWithoutAlpha clone() const pure nothrow @trusted {
|
||||
auto n = new TrueColorImageWithoutAlpha(width, height);
|
||||
n.imageData.bytes[] = this.imageData.bytes[]; // copy into existing array ctor allocated
|
||||
return n;
|
||||
}
|
||||
|
||||
/// .
|
||||
override int width() const pure nothrow @trusted @nogc { return _width; }
|
||||
///.
|
||||
override int height() const pure nothrow @trusted @nogc { return _height; }
|
||||
|
||||
override Color getPixel(int x, int y) const pure nothrow @trusted @nogc {
|
||||
if (x >= 0 && y >= 0 && x < _width && y < _height) {
|
||||
uint pos = (y*_width+x) * 3;
|
||||
return Color(imageData.bytes[0], imageData.bytes[1], imageData.bytes[2], 255);
|
||||
} else {
|
||||
return Color(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
override void setPixel(int x, int y, in Color clr) nothrow @trusted {
|
||||
if (x >= 0 && y >= 0 && x < _width && y < _height) {
|
||||
uint pos = y*_width+x;
|
||||
//if (pos < imageData.bytes.length/4) imageData.colors.ptr[pos] = clr;
|
||||
// FIXME
|
||||
}
|
||||
}
|
||||
|
||||
/// .
|
||||
this(int w, int h) pure nothrow @safe {
|
||||
_width = w;
|
||||
_height = h;
|
||||
imageData.bytes = new ubyte[w*h*3];
|
||||
}
|
||||
|
||||
/// Creates with existing data. The data pointer is stored here.
|
||||
this(int w, int h, ubyte[] data) pure nothrow @safe {
|
||||
_width = w;
|
||||
_height = h;
|
||||
assert(data.length == w * h * 3);
|
||||
imageData.bytes = data;
|
||||
}
|
||||
|
||||
///
|
||||
override TrueColorImage getAsTrueColorImage() pure nothrow @safe {
|
||||
// FIXME
|
||||
//return this;
|
||||
}
|
||||
}
|
||||
+/
|
||||
|
||||
|
||||
alias extern(C) int function(const void*, const void*) @system Comparator;
|
||||
@trusted void nonPhobosSort(T)(T[] obj, Comparator comparator) {
|
||||
import core.stdc.stdlib;
|
||||
|
|
12
csv.d
12
csv.d
|
@ -6,9 +6,10 @@ import std.array;
|
|||
|
||||
///
|
||||
string[][] readCsv(string data) {
|
||||
data = data.replace("\r\n", "\n");
|
||||
data = data.replace("\r", "");
|
||||
|
||||
auto idx = data.indexOf("\n");
|
||||
//auto idx = data.indexOf("\n");
|
||||
//data = data[idx + 1 .. $]; // skip headers
|
||||
|
||||
string[] fields;
|
||||
|
@ -39,9 +40,9 @@ string[][] readCsv(string data) {
|
|||
field ~= c;
|
||||
break;
|
||||
case 1: // in quote
|
||||
if(c == '"')
|
||||
if(c == '"') {
|
||||
state = 2;
|
||||
else
|
||||
} else
|
||||
field ~= c;
|
||||
break;
|
||||
case 2: // is it a closing quote or an escaped one?
|
||||
|
@ -55,6 +56,11 @@ string[][] readCsv(string data) {
|
|||
}
|
||||
}
|
||||
|
||||
if(field !is null)
|
||||
current ~= field;
|
||||
if(current !is null)
|
||||
records ~= current;
|
||||
|
||||
|
||||
return records;
|
||||
}
|
||||
|
|
55
dom.d
55
dom.d
|
@ -1,4 +1,8 @@
|
|||
// FIXME: add classList
|
||||
// FIXME: add matchesSelector
|
||||
// FIXME: https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
|
||||
// FIXME: appendChild should not fail if the thing already has a parent; it should just automatically remove it per standard.
|
||||
|
||||
/++
|
||||
This is an html DOM implementation, started with cloning
|
||||
what the browser offers in Javascript, but going well beyond
|
||||
|
@ -71,7 +75,7 @@ bool isConvenientAttribute(string name) {
|
|||
/// The main document interface, including a html parser.
|
||||
class Document : FileResource {
|
||||
/// Convenience method for web scraping. Requires [arsd.http2] to be
|
||||
/// included in the build.
|
||||
/// included in the build as well as [arsd.characterencodings].
|
||||
static Document fromUrl()(string url) {
|
||||
import arsd.http2;
|
||||
auto client = new HttpClient();
|
||||
|
@ -79,7 +83,10 @@ class Document : FileResource {
|
|||
auto req = client.navigateTo(Uri(url), HttpVerb.GET);
|
||||
auto res = req.waitForCompletion();
|
||||
|
||||
return new Document(cast(string) res.content);
|
||||
auto document = new Document();
|
||||
document.parseGarbage(cast(string) res.content);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
///.
|
||||
|
@ -821,6 +828,12 @@ class Document : FileResource {
|
|||
|
||||
while(pos < data.length && data[pos] != '>')
|
||||
pos++;
|
||||
|
||||
if(pos >= data.length) {
|
||||
// the tag never closed
|
||||
assert(data.length != 0);
|
||||
pos = data.length - 1; // rewinding so it hits the end at the bottom..
|
||||
}
|
||||
}
|
||||
|
||||
auto whereThisTagStarted = pos; // for better error messages
|
||||
|
@ -1403,7 +1416,7 @@ class Element {
|
|||
body {
|
||||
auto e = cast(SomeElementType) getElementById(id);
|
||||
if(e is null)
|
||||
throw new ElementNotFoundException(SomeElementType.stringof, "id=" ~ id, file, line);
|
||||
throw new ElementNotFoundException(SomeElementType.stringof, "id=" ~ id, this, file, line);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
@ -1418,7 +1431,7 @@ class Element {
|
|||
body {
|
||||
auto e = cast(SomeElementType) querySelector(selector);
|
||||
if(e is null)
|
||||
throw new ElementNotFoundException(SomeElementType.stringof, selector, file, line);
|
||||
throw new ElementNotFoundException(SomeElementType.stringof, selector, this, file, line);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
@ -2127,7 +2140,7 @@ class Element {
|
|||
static if(!is(T == Element)) {
|
||||
auto t = cast(T) par;
|
||||
if(t is null)
|
||||
throw new ElementNotFoundException("", tagName ~ " parent not found");
|
||||
throw new ElementNotFoundException("", tagName ~ " parent not found", this);
|
||||
} else
|
||||
auto t = par;
|
||||
|
||||
|
@ -3494,8 +3507,14 @@ struct ElementCollection {
|
|||
return !elements.length;
|
||||
}
|
||||
|
||||
/// Collects strings from the collection, concatenating them together
|
||||
/// Kinda like running reduce and ~= on it.
|
||||
/++
|
||||
Collects strings from the collection, concatenating them together
|
||||
Kinda like running reduce and ~= on it.
|
||||
|
||||
---
|
||||
document["p"].collect!"innerText";
|
||||
---
|
||||
+/
|
||||
string collect(string method)(string separator = "") {
|
||||
string text;
|
||||
foreach(e; elements) {
|
||||
|
@ -3514,6 +3533,17 @@ struct ElementCollection {
|
|||
return this;
|
||||
}
|
||||
|
||||
/++
|
||||
Calls [Element.wrapIn] on each member of the collection, but clones the argument `what` for each one.
|
||||
+/
|
||||
ElementCollection wrapIn(Element what) {
|
||||
foreach(e; elements) {
|
||||
e.wrapIn(what.cloneNode(false));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Concatenates two ElementCollection together.
|
||||
ElementCollection opBinary(string op : "~")(ElementCollection rhs) {
|
||||
return ElementCollection(this.elements ~ rhs.elements);
|
||||
|
@ -3776,7 +3806,7 @@ T require(T = Element, string file = __FILE__, int line = __LINE__)(Element e) i
|
|||
body {
|
||||
auto ret = cast(T) e;
|
||||
if(ret is null)
|
||||
throw new ElementNotFoundException(T.stringof, "passed value", file, line);
|
||||
throw new ElementNotFoundException(T.stringof, "passed value", e, file, line);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -5042,7 +5072,7 @@ class Table : Element {
|
|||
return position;
|
||||
}
|
||||
|
||||
foreach(int i, rowElement; rows) {
|
||||
foreach(i, rowElement; rows) {
|
||||
auto row = cast(TableRow) rowElement;
|
||||
assert(row !is null);
|
||||
assert(i < ret.length);
|
||||
|
@ -5059,7 +5089,7 @@ class Table : Element {
|
|||
foreach(int j; 0 .. cell.colspan) {
|
||||
foreach(int k; 0 .. cell.rowspan)
|
||||
// if the first row, always append.
|
||||
insertCell(k + i, k == 0 ? -1 : position, cell);
|
||||
insertCell(k + cast(int) i, k == 0 ? -1 : position, cell);
|
||||
position++;
|
||||
}
|
||||
}
|
||||
|
@ -5139,9 +5169,12 @@ class MarkupException : Exception {
|
|||
class ElementNotFoundException : Exception {
|
||||
|
||||
/// type == kind of element you were looking for and search == a selector describing the search.
|
||||
this(string type, string search, string file = __FILE__, size_t line = __LINE__) {
|
||||
this(string type, string search, Element searchContext, string file = __FILE__, size_t line = __LINE__) {
|
||||
this.searchContext = searchContext;
|
||||
super("Element of type '"~type~"' matching {"~search~"} not found.", file, line);
|
||||
}
|
||||
|
||||
Element searchContext;
|
||||
}
|
||||
|
||||
/// The html struct is used to differentiate between regular text nodes and html in certain functions
|
||||
|
|
6
dub.json
6
dub.json
|
@ -95,6 +95,8 @@
|
|||
"description": "MySQL client library. Wraps the official C library with my database.d interface.",
|
||||
"targetType": "sourceLibrary",
|
||||
"dependencies": {"arsd-official:database_base":"*"},
|
||||
"libs-posix": ["mysqlclient"],
|
||||
"libs-windows": ["libmysql"],
|
||||
"sourceFiles": ["mysql.d"]
|
||||
},
|
||||
{
|
||||
|
@ -102,6 +104,7 @@
|
|||
"description": "Postgresql client library. Wraps the libpq C library with my database.d interface.",
|
||||
"targetType": "sourceLibrary",
|
||||
"dependencies": {"arsd-official:database_base":"*"},
|
||||
"libs": ["pq"],
|
||||
"sourceFiles": ["postgres.d"]
|
||||
},
|
||||
|
||||
|
@ -110,6 +113,8 @@
|
|||
"description": "sqlite wrapper. Wraps the official C library with my database.d interface.",
|
||||
"targetType": "sourceLibrary",
|
||||
"dependencies": {"arsd-official:database_base":"*"},
|
||||
"libs": ["sqlite3"],
|
||||
"libs-posix": ["dl"],
|
||||
"sourceFiles": ["sqlite.d"]
|
||||
},
|
||||
|
||||
|
@ -118,6 +123,7 @@
|
|||
"description": "Microsoft SQL Server client library. Wraps the official ODBC library with my database.d interface.",
|
||||
"targetType": "sourceLibrary",
|
||||
"dependencies": {"arsd-official:database_base":"*"},
|
||||
"libs-windows": ["odbc32"],
|
||||
"sourceFiles": ["mssql.d"]
|
||||
},
|
||||
|
||||
|
|
|
@ -31,9 +31,10 @@
|
|||
}
|
||||
|
||||
int x, y;
|
||||
override void update(Duration deltaTime) {
|
||||
override bool update(Duration deltaTime) {
|
||||
x += 1;
|
||||
y += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
override SimpleWindow getWindow() {
|
||||
|
@ -100,7 +101,8 @@ class GameHelperBase {
|
|||
abstract void drawFrame();
|
||||
|
||||
/// Implement this to update. The deltaTime tells how much real time has passed since the last update.
|
||||
abstract void update(Duration deltaTime);
|
||||
/// Returns true if anything changed, which will queue up a redraw
|
||||
abstract bool update(Duration deltaTime);
|
||||
//abstract void fillAudioBuffer(short[] buffer);
|
||||
|
||||
/// Returns the main game window. This function will only be
|
||||
|
@ -155,6 +157,14 @@ void runGame(T : GameHelperBase)(T game, int maxUpdateRate = 20, int maxRedrawRa
|
|||
|
||||
delegate (KeyEvent ke) {
|
||||
game.keyboardState[ke.hardwareCode] = ke.pressed;
|
||||
/*
|
||||
switch(ke.key) {
|
||||
case Key.UpArrow:
|
||||
game.joysticks[0]
|
||||
break;
|
||||
default:
|
||||
}
|
||||
*/
|
||||
// FIXME
|
||||
}
|
||||
);
|
||||
|
|
313
htmltotext.d
313
htmltotext.d
|
@ -7,10 +7,47 @@ import std.string;
|
|||
|
||||
import std.uni : isWhite;
|
||||
|
||||
///
|
||||
class HtmlConverter {
|
||||
int width;
|
||||
|
||||
/++
|
||||
Will enable color output using VT codes. Determines color through dom.d's css support, which means you need to apply a stylesheet first.
|
||||
|
||||
---
|
||||
import arsd.dom;
|
||||
|
||||
auto document = new Document(source_code_for_html);
|
||||
auto stylesheet = new Stylesheet(source_code_for_css);
|
||||
stylesheet.apply(document);
|
||||
---
|
||||
+/
|
||||
bool enableVtOutput;
|
||||
|
||||
|
||||
string color;
|
||||
string backgroundColor;
|
||||
|
||||
///
|
||||
void htmlToText(Element element, bool preformatted, int width) {
|
||||
string color, backgroundColor;
|
||||
if(enableVtOutput) {
|
||||
color = element.computedStyle.getValue("color");
|
||||
backgroundColor = element.computedStyle.getValue("background-color");
|
||||
}
|
||||
|
||||
string originalColor = this.color, originalBackgroundColor = this.backgroundColor;
|
||||
|
||||
this.color = color.length ? color : this.color;
|
||||
this.backgroundColor = backgroundColor.length ? backgroundColor : this.backgroundColor;
|
||||
|
||||
scope(exit) {
|
||||
// the idea is as we pop working back up the tree, it restores what it was here
|
||||
this.color = originalColor;
|
||||
this.backgroundColor = originalBackgroundColor;
|
||||
}
|
||||
|
||||
|
||||
this.width = width;
|
||||
if(auto tn = cast(TextNode) element) {
|
||||
foreach(dchar ch; tn.nodeValue) {
|
||||
|
@ -28,7 +65,7 @@ class HtmlConverter {
|
|||
// The table stuff is removed right now because while it looks
|
||||
// ok for test tables, it isn't working well for the emails I have
|
||||
// - it handles data ok but not really nested layouts.
|
||||
case "trfixme":
|
||||
case "trlol":
|
||||
auto children = element.childElements;
|
||||
|
||||
auto tdWidth = (width - cast(int)(children.length)*3) / cast(int)(children.length);
|
||||
|
@ -91,6 +128,16 @@ class HtmlConverter {
|
|||
s ~= "\n";
|
||||
}
|
||||
break;
|
||||
case "tr":
|
||||
startBlock(2);
|
||||
sinkChildren();
|
||||
endBlock();
|
||||
break;
|
||||
case "td":
|
||||
startBlock(0);
|
||||
sinkChildren();
|
||||
endBlock();
|
||||
break;
|
||||
case "a":
|
||||
sinkChildren();
|
||||
if(element.href != element.innerText) {
|
||||
|
@ -105,17 +152,27 @@ class HtmlConverter {
|
|||
}
|
||||
break;
|
||||
case "span":
|
||||
/*
|
||||
auto csc = element.computedStyle.getValue("color");
|
||||
if(csc.length) {
|
||||
auto c = Color.fromString(csc);
|
||||
s ~= format("\033[38;2;%d;%d;%dm", c.r, c.g, c.b);
|
||||
}
|
||||
sinkChildren();
|
||||
if(enableVtOutput) {
|
||||
auto csc = color; // element.computedStyle.getValue("color");
|
||||
if(csc.length) {
|
||||
auto c = Color.fromString(csc);
|
||||
s ~= format("\033[38;2;%d;%d;%dm", c.r, c.g, c.b);
|
||||
}
|
||||
|
||||
if(csc.length)
|
||||
s ~= "\033[39m";
|
||||
*/
|
||||
bool bold = element.computedStyle.getValue("font-weight") == "bold";
|
||||
|
||||
if(bold)
|
||||
s ~= "\033[1m";
|
||||
|
||||
sinkChildren();
|
||||
|
||||
if(bold)
|
||||
s ~= "\033[0m";
|
||||
if(csc.length)
|
||||
s ~= "\033[39m";
|
||||
} else {
|
||||
sinkChildren();
|
||||
}
|
||||
break;
|
||||
case "p":
|
||||
startBlock();
|
||||
|
@ -126,9 +183,15 @@ class HtmlConverter {
|
|||
case "em", "i":
|
||||
if(element.innerText.length == 0)
|
||||
break;
|
||||
sink('*', false);
|
||||
sinkChildren();
|
||||
sink('*', false);
|
||||
if(enableVtOutput) {
|
||||
s ~= "\033[1m";
|
||||
sinkChildren();
|
||||
s ~= "\033[0m";
|
||||
} else {
|
||||
sink('*', false);
|
||||
sinkChildren();
|
||||
sink('*', false);
|
||||
}
|
||||
break;
|
||||
case "u":
|
||||
if(element.innerText.length == 0)
|
||||
|
@ -139,20 +202,28 @@ class HtmlConverter {
|
|||
break;
|
||||
case "ul":
|
||||
ulDepth++;
|
||||
startBlock(2);
|
||||
sinkChildren();
|
||||
endBlock();
|
||||
ulDepth--;
|
||||
break;
|
||||
case "ol":
|
||||
olDepth++;
|
||||
startBlock(2);
|
||||
sinkChildren();
|
||||
endBlock();
|
||||
olDepth--;
|
||||
break;
|
||||
case "li":
|
||||
startBlock();
|
||||
|
||||
//sink('\t', true);
|
||||
sink(' ', true);
|
||||
sink(' ', true);
|
||||
/*
|
||||
foreach(cnt; 0 .. olDepth + ulDepth) {
|
||||
sink(' ', true);
|
||||
sink(' ', true);
|
||||
}
|
||||
*/
|
||||
if(olDepth)
|
||||
sink('*', false);
|
||||
if(ulDepth)
|
||||
|
@ -164,15 +235,33 @@ class HtmlConverter {
|
|||
endBlock();
|
||||
break;
|
||||
|
||||
case "h1", "h2":
|
||||
case "dl":
|
||||
case "dt":
|
||||
case "dd":
|
||||
startBlock(element.tagName == "dd" ? 2 : 0);
|
||||
sinkChildren();
|
||||
endBlock();
|
||||
break;
|
||||
|
||||
case "h1":
|
||||
startBlock();
|
||||
sink('#', true);
|
||||
sink('#', true);
|
||||
sink(' ', true);
|
||||
sinkChildren();
|
||||
sink(' ', true);
|
||||
sink('#', true);
|
||||
sink('#', true);
|
||||
endBlock();
|
||||
break;
|
||||
case "h2", "h3":
|
||||
startBlock();
|
||||
sinkChildren();
|
||||
sink('\n', true);
|
||||
foreach(dchar ch; element.innerText)
|
||||
sink(element.tagName == "h1" ? '=' : '-', false);
|
||||
sink(element.tagName == "h2" ? '=' : '-', false);
|
||||
endBlock();
|
||||
break;
|
||||
|
||||
case "hr":
|
||||
startBlock();
|
||||
foreach(i; 0 .. width / 4)
|
||||
|
@ -185,7 +274,6 @@ class HtmlConverter {
|
|||
case "br":
|
||||
sink('\n', true);
|
||||
break;
|
||||
case "tr":
|
||||
case "div":
|
||||
startBlock();
|
||||
|
||||
|
@ -207,7 +295,7 @@ class HtmlConverter {
|
|||
endBlock();
|
||||
break;
|
||||
case "pre":
|
||||
startBlock();
|
||||
startBlock(4);
|
||||
foreach(child; element.childNodes)
|
||||
htmlToText(child, true, width);
|
||||
endBlock();
|
||||
|
@ -221,6 +309,7 @@ class HtmlConverter {
|
|||
int olDepth;
|
||||
int ulDepth;
|
||||
|
||||
///
|
||||
string convert(string html, bool wantWordWrap = true, int wrapAmount = 74) {
|
||||
Document document = new Document;
|
||||
|
||||
|
@ -237,10 +326,16 @@ class HtmlConverter {
|
|||
//auto stylesheet = new StyleSheet(readText("/var/www/dpldocs.info/experimental-docs/style.css"));
|
||||
//stylesheet.apply(document);
|
||||
|
||||
return convert(start, wantWordWrap, wrapAmount);
|
||||
}
|
||||
|
||||
///
|
||||
string convert(Element start, bool wantWordWrap = true, int wrapAmount = 74) {
|
||||
htmlToText(start, false, wrapAmount);
|
||||
return s;
|
||||
}
|
||||
|
||||
///
|
||||
void reset() {
|
||||
s = null;
|
||||
justOutputWhitespace = true;
|
||||
|
@ -248,6 +343,7 @@ class HtmlConverter {
|
|||
justOutputMargin = true;
|
||||
}
|
||||
|
||||
///
|
||||
string s;
|
||||
bool justOutputWhitespace = true;
|
||||
bool justOutputBlock = true;
|
||||
|
@ -255,6 +351,12 @@ class HtmlConverter {
|
|||
int lineLength;
|
||||
|
||||
void sink(dchar item, bool preformatted, int lineWidthOverride = int.min) {
|
||||
|
||||
if(needsIndent && item != '\n') {
|
||||
lineLength += doIndent();
|
||||
needsIndent = false;
|
||||
}
|
||||
|
||||
int width = lineWidthOverride == int.min ? this.width : lineWidthOverride;
|
||||
if(!preformatted && isWhite(item)) {
|
||||
if(!justOutputWhitespace) {
|
||||
|
@ -282,8 +384,9 @@ class HtmlConverter {
|
|||
auto os = s;
|
||||
s = os[0 .. idx];
|
||||
s ~= '\n';
|
||||
s ~= os[idx + 1 .. $];
|
||||
lineLength = cast(int)(os[idx+1..$].length);
|
||||
lineLength += doIndent();
|
||||
s ~= os[idx + 1 .. $];
|
||||
broken = true;
|
||||
break;
|
||||
}
|
||||
|
@ -295,15 +398,17 @@ class HtmlConverter {
|
|||
if(!broken) {
|
||||
s ~= '\n';
|
||||
lineLength = 0;
|
||||
needsIndent = true;
|
||||
justOutputWhitespace = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if(item == '\n')
|
||||
if(item == '\n') {
|
||||
lineLength = 0;
|
||||
else
|
||||
needsIndent = true;
|
||||
} else
|
||||
lineLength ++;
|
||||
|
||||
|
||||
|
@ -312,163 +417,53 @@ class HtmlConverter {
|
|||
justOutputMargin = false;
|
||||
}
|
||||
}
|
||||
void startBlock() {
|
||||
|
||||
int doIndent() {
|
||||
int cnt = 0;
|
||||
foreach(i; indentStack)
|
||||
foreach(lol; 0 .. i) {
|
||||
s ~= ' ';
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int[] indentStack;
|
||||
bool needsIndent = false;
|
||||
|
||||
void startBlock(int indent = 0) {
|
||||
|
||||
indentStack ~= indent;
|
||||
|
||||
if(!justOutputBlock) {
|
||||
s ~= "\n";
|
||||
lineLength = 0;
|
||||
needsIndent = true;
|
||||
justOutputBlock = true;
|
||||
}
|
||||
if(!justOutputMargin) {
|
||||
s ~= "\n";
|
||||
lineLength = 0;
|
||||
needsIndent = true;
|
||||
justOutputMargin = true;
|
||||
}
|
||||
}
|
||||
void endBlock() {
|
||||
if(indentStack.length)
|
||||
indentStack = indentStack[0 .. $ - 1];
|
||||
|
||||
if(!justOutputMargin) {
|
||||
s ~= "\n";
|
||||
lineLength = 0;
|
||||
needsIndent = true;
|
||||
justOutputMargin = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
string htmlToText(string html, bool wantWordWrap = true, int wrapAmount = 74) {
|
||||
auto converter = new HtmlConverter();
|
||||
return converter.convert(html, true, wrapAmount);
|
||||
}
|
||||
|
||||
string repeat(string s, ulong num) {
|
||||
string ret;
|
||||
foreach(i; 0 .. num)
|
||||
ret ~= s;
|
||||
return ret;
|
||||
}
|
||||
|
||||
import std.stdio;
|
||||
version(none)
|
||||
void penis() {
|
||||
|
||||
again:
|
||||
string result = "";
|
||||
foreach(ele; start.tree) {
|
||||
if(ele is start) continue;
|
||||
if(ele.nodeType != 1) continue;
|
||||
|
||||
switch(ele.tagName) {
|
||||
goto again;
|
||||
case "h1":
|
||||
ele.innerText = "\r" ~ ele.innerText ~ "\n" ~ repeat("=", ele.innerText.length) ~ "\r";
|
||||
ele.stripOut();
|
||||
goto again;
|
||||
case "h2":
|
||||
ele.innerText = "\r" ~ ele.innerText ~ "\n" ~ repeat("-", ele.innerText.length) ~ "\r";
|
||||
ele.stripOut();
|
||||
goto again;
|
||||
case "h3":
|
||||
ele.innerText = "\r" ~ ele.innerText.toUpper ~ "\r";
|
||||
ele.stripOut();
|
||||
goto again;
|
||||
case "td":
|
||||
case "p":
|
||||
/*
|
||||
if(ele.innerHTML.length > 1)
|
||||
ele.innerHTML = "\r" ~ wrap(ele.innerHTML) ~ "\r";
|
||||
ele.stripOut();
|
||||
goto again;
|
||||
*/
|
||||
break;
|
||||
case "a":
|
||||
string href = ele.getAttribute("href");
|
||||
if(href && !ele.hasClass("no-brackets")) {
|
||||
if(ele.hasClass("href-text"))
|
||||
ele.innerText = href;
|
||||
else {
|
||||
if(ele.innerText != href)
|
||||
ele.innerText = ele.innerText ~ " <" ~ href ~ "> ";
|
||||
}
|
||||
}
|
||||
ele.stripOut();
|
||||
goto again;
|
||||
case "ol":
|
||||
case "ul":
|
||||
ele.innerHTML = "\r" ~ ele.innerHTML ~ "\r";
|
||||
break;
|
||||
case "li":
|
||||
if(!ele.innerHTML.startsWith("* "))
|
||||
ele.innerHTML = "* " ~ ele.innerHTML ~ "\r";
|
||||
// ele.stripOut();
|
||||
break;
|
||||
case "sup":
|
||||
ele.innerText = "^" ~ ele.innerText;
|
||||
ele.stripOut();
|
||||
break;
|
||||
/*
|
||||
case "img":
|
||||
string alt = ele.getAttribute("alt");
|
||||
if(alt)
|
||||
result ~= ele.alt;
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
ele.stripOut();
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
again2:
|
||||
//start.innerHTML = start.innerHTML().replace("\u0001", "\n");
|
||||
|
||||
foreach(ele; start.tree) {
|
||||
if(ele.tagName == "td") {
|
||||
if(ele.directText().strip().length) {
|
||||
ele.prependText("\r");
|
||||
ele.appendText("\r");
|
||||
}
|
||||
ele.stripOut();
|
||||
goto again2;
|
||||
} else if(ele.tagName == "p") {
|
||||
if(strip(ele.innerText()).length > 1) {
|
||||
string res = "";
|
||||
string all = ele.innerText().replace("\n \n", "\n\n");
|
||||
foreach(part; all.split("\n\n"))
|
||||
res ~= "\r" ~ strip( wantWordWrap ? wrap(part, /*74*/ wrapAmount) : part ) ~ "\r";
|
||||
ele.innerText = res;
|
||||
} else
|
||||
ele.innerText = strip(ele.innerText);
|
||||
ele.stripOut();
|
||||
goto again2;
|
||||
} else if(ele.tagName == "li") {
|
||||
auto part = ele.innerText;
|
||||
part = strip( wantWordWrap ? wrap(part, wrapAmount - 2) : part );
|
||||
part = " " ~ part.replace("\n", "\n\v") ~ "\r";
|
||||
ele.innerText = part;
|
||||
ele.stripOut();
|
||||
goto again2;
|
||||
}
|
||||
}
|
||||
|
||||
result = start.innerText();
|
||||
result = squeeze(result, " ");
|
||||
|
||||
result = result.replace("\r ", "\r");
|
||||
result = result.replace(" \r", "\r");
|
||||
|
||||
//result = result.replace("\u00a0", " ");
|
||||
|
||||
|
||||
result = squeeze(result, "\r");
|
||||
result = result.replace("\r", "\n\n");
|
||||
|
||||
result = result.replace("\v", " ");
|
||||
|
||||
result = result.replace("舗", "'"); // HACK: this shouldn't be needed, but apparently is in practice surely due to a bug elsewhere
|
||||
result = result.replace(""", "\""); // HACK: this shouldn't be needed, but apparently is in practice surely due to a bug elsewhere
|
||||
//result = htmlEntitiesDecode(result); // for special chars mainly
|
||||
|
||||
result = result.replace("\u0001 ", "\n");
|
||||
result = result.replace("\u0001", "\n");
|
||||
|
||||
//a = std.regex.replace(a, std.regex.regex("(\n\t)+", "g"), "\n"); //\t");
|
||||
return result.strip;
|
||||
}
|
||||
|
|
2
http.d
2
http.d
|
@ -3,7 +3,7 @@
|
|||
|
||||
I no longer work on this, use http2.d instead.
|
||||
+/
|
||||
module arsd.http;
|
||||
deprecated module arsd.http;
|
||||
|
||||
import std.socket;
|
||||
|
||||
|
|
15
http2.d
15
http2.d
|
@ -75,6 +75,20 @@ HttpRequest get(string url) {
|
|||
return request;
|
||||
}
|
||||
|
||||
HttpRequest post(string url, string[string] req) {
|
||||
auto client = new HttpClient();
|
||||
ubyte[] bdata;
|
||||
foreach(k, v; req) {
|
||||
if(bdata.length)
|
||||
bdata ~= cast(ubyte[]) "&";
|
||||
bdata ~= cast(ubyte[]) encodeComponent(k);
|
||||
bdata ~= cast(ubyte[]) "=";
|
||||
bdata ~= cast(ubyte[]) encodeComponent(v);
|
||||
}
|
||||
auto request = client.request(Uri(url), HttpVerb.POST, bdata, "application/x-www-form-urlencoded");
|
||||
return request;
|
||||
}
|
||||
|
||||
/// gets the text off a url. basic operation only.
|
||||
string getText(string url) {
|
||||
auto request = get(url);
|
||||
|
@ -1090,7 +1104,6 @@ class HttpRequest {
|
|||
ubyte[] sendBuffer;
|
||||
|
||||
HttpResponse responseData;
|
||||
HttpRequestParameters parameters;
|
||||
private HttpClient parentClient;
|
||||
|
||||
size_t bodyBytesSent;
|
||||
|
|
17
joystick.d
17
joystick.d
|
@ -167,6 +167,8 @@ version(linux) {
|
|||
// I'd just use my xbox controller.
|
||||
);
|
||||
|
||||
/// For Linux only, reads the latest joystick events into the change buffer, if available.
|
||||
/// It is non-blocking
|
||||
void readJoystickEvents(int fd) {
|
||||
js_event event;
|
||||
|
||||
|
@ -312,6 +314,7 @@ int enableJoystickInput(
|
|||
// return 0;
|
||||
}
|
||||
|
||||
///
|
||||
void closeJoysticks() {
|
||||
version(linux) {
|
||||
foreach(ref fd; joystickFds) {
|
||||
|
@ -330,33 +333,36 @@ void closeJoysticks() {
|
|||
} else static assert(0);
|
||||
}
|
||||
|
||||
///
|
||||
struct JoystickUpdate {
|
||||
///
|
||||
int player;
|
||||
|
||||
JoystickState old;
|
||||
JoystickState current;
|
||||
|
||||
// changes from last update
|
||||
/// changes from last update
|
||||
bool buttonWasJustPressed(Button button) {
|
||||
return buttonIsPressed(button) && !oldButtonIsPressed(button);
|
||||
}
|
||||
|
||||
/// ditto
|
||||
bool buttonWasJustReleased(Button button) {
|
||||
return !buttonIsPressed(button) && oldButtonIsPressed(button);
|
||||
}
|
||||
|
||||
// this is normalized down to a 16 step change
|
||||
// and ignores a dead zone near the middle
|
||||
/// this is normalized down to a 16 step change
|
||||
/// and ignores a dead zone near the middle
|
||||
short axisChange(Axis axis) {
|
||||
return cast(short) (axisPosition(axis) - oldAxisPosition(axis));
|
||||
}
|
||||
|
||||
// current state
|
||||
/// current state
|
||||
bool buttonIsPressed(Button button) {
|
||||
return buttonIsPressedHelper(button, ¤t);
|
||||
}
|
||||
|
||||
// Note: UP is negative!
|
||||
/// Note: UP is negative!
|
||||
short axisPosition(Axis axis, short digitalFallbackValue = short.max) {
|
||||
return axisPositionHelper(axis, ¤t, digitalFallbackValue);
|
||||
}
|
||||
|
@ -521,6 +527,7 @@ struct JoystickUpdate {
|
|||
}
|
||||
}
|
||||
|
||||
///
|
||||
JoystickUpdate getJoystickUpdate(int player) {
|
||||
static JoystickState[4] previous;
|
||||
|
||||
|
|
21
jpeg.d
21
jpeg.d
|
@ -2971,9 +2971,10 @@ public bool detect_jpeg_image_from_file (const(char)[] filename, out int width,
|
|||
bool m_eof_flag, m_error_flag;
|
||||
|
||||
if (filename.length == 0) throw new Exception("cannot open unnamed file");
|
||||
if (filename.length < 2048) {
|
||||
import core.stdc.stdlib : alloca;
|
||||
auto tfn = (cast(char*)alloca(filename.length+1))[0..filename.length+1];
|
||||
if (filename.length < 512) {
|
||||
char[513] buffer;
|
||||
//import core.stdc.stdlib : alloca;
|
||||
auto tfn = buffer[0 .. filename.length + 1]; // (cast(char*)alloca(filename.length+1))[0..filename.length+1];
|
||||
tfn[0..filename.length] = filename[];
|
||||
tfn[filename.length] = 0;
|
||||
m_pFile = fopen(tfn.ptr, "rb");
|
||||
|
@ -3147,9 +3148,10 @@ public ubyte[] decompress_jpeg_image_from_file(bool useMalloc=false) (const(char
|
|||
bool m_eof_flag, m_error_flag;
|
||||
|
||||
if (filename.length == 0) throw new Exception("cannot open unnamed file");
|
||||
if (filename.length < 2048) {
|
||||
import core.stdc.stdlib : alloca;
|
||||
auto tfn = (cast(char*)alloca(filename.length+1))[0..filename.length+1];
|
||||
if (filename.length < 512) {
|
||||
char[513] buffer;
|
||||
//import core.stdc.stdlib : alloca;
|
||||
auto tfn = buffer[0 .. filename.length + 1]; // (cast(char*)alloca(filename.length+1))[0..filename.length+1];
|
||||
tfn[0..filename.length] = filename[];
|
||||
tfn[filename.length] = 0;
|
||||
m_pFile = fopen(tfn.ptr, "rb");
|
||||
|
@ -3338,9 +3340,10 @@ public MemoryImage readJpeg (const(char)[] filename) {
|
|||
bool m_eof_flag, m_error_flag;
|
||||
|
||||
if (filename.length == 0) throw new Exception("cannot open unnamed file");
|
||||
if (filename.length < 2048) {
|
||||
import core.stdc.stdlib : alloca;
|
||||
auto tfn = (cast(char*)alloca(filename.length+1))[0..filename.length+1];
|
||||
if (filename.length < 512) {
|
||||
char[513] buffer;
|
||||
//import core.stdc.stdlib : alloca;
|
||||
auto tfn = buffer[0 .. filename.length + 1]; // (cast(char*)alloca(filename.length+1))[0..filename.length+1];
|
||||
tfn[0..filename.length] = filename[];
|
||||
tfn[filename.length] = 0;
|
||||
m_pFile = fopen(tfn.ptr, "rb");
|
||||
|
|
2
jsvar.d
2
jsvar.d
|
@ -709,7 +709,7 @@ struct var {
|
|||
case Type.String:
|
||||
case Type.Function:
|
||||
assert(0); // FIXME
|
||||
break;
|
||||
//break;
|
||||
case Type.Integral:
|
||||
return var(-this.get!long);
|
||||
case Type.Floating:
|
||||
|
|
35
minigui.d
35
minigui.d
|
@ -1,5 +1,16 @@
|
|||
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775498%28v=vs.85%29.aspx
|
||||
|
||||
// So a window needs to have a selection, and that can be represented by a type. This is manipulated by various
|
||||
// functions like cut, copy, paste. Widgets can have a selection and that would assert teh selection ownership for
|
||||
// the window.
|
||||
|
||||
// so what about context menus?
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/Controls/about-custom-draw
|
||||
|
||||
// FIXME: add a command search thingy built in and implement tip.
|
||||
// FIXME: omg omg what if menu functions have arguments and it can pop up a gui or command line script them?!
|
||||
|
||||
// On Windows:
|
||||
// FIXME: various labels look broken in high contrast mode
|
||||
// FIXME: changing themes while the program is upen doesn't trigger a redraw
|
||||
|
@ -3910,8 +3921,10 @@ class MainWindow : Window {
|
|||
.toolbar toolbar;
|
||||
bool separator;
|
||||
.accelerator accelerator;
|
||||
.hotkey hotkey;
|
||||
.icon icon;
|
||||
string label;
|
||||
string tip;
|
||||
foreach(attr; __traits(getAttributes, __traits(getMember, T, memberName))) {
|
||||
static if(is(typeof(attr) == .menu))
|
||||
menu = attr;
|
||||
|
@ -3921,10 +3934,14 @@ class MainWindow : Window {
|
|||
separator = true;
|
||||
else static if(is(typeof(attr) == .accelerator))
|
||||
accelerator = attr;
|
||||
else static if(is(typeof(attr) == .hotkey))
|
||||
hotkey = attr;
|
||||
else static if(is(typeof(attr) == .icon))
|
||||
icon = attr;
|
||||
else static if(is(typeof(attr) == .label))
|
||||
label = attr.label;
|
||||
else static if(is(typeof(attr) == .tip))
|
||||
tip = attr.tip;
|
||||
}
|
||||
|
||||
if(menu !is .menu.init || toolbar !is .toolbar.init) {
|
||||
|
@ -5504,6 +5521,15 @@ abstract class EditableTextWidget : EditableTextWidgetParent {
|
|||
super(parent);
|
||||
}
|
||||
|
||||
bool wordWrapEnabled_ = false;
|
||||
void wordWrapEnabled(bool enabled) {
|
||||
version(win32_widgets) {
|
||||
SendMessageW(hwnd, EM_FMTLINES, enabled ? 1 : 0, 0);
|
||||
} else version(custom_widgets) {
|
||||
wordWrapEnabled_ = enabled; // FIXME
|
||||
} else static assert(false);
|
||||
}
|
||||
|
||||
override int minWidth() { return 16; }
|
||||
override int minHeight() { return Window.lineHeight + 0; } // the +0 is to leave room for the padding
|
||||
override int widthStretchiness() { return 7; }
|
||||
|
@ -5563,6 +5589,7 @@ abstract class EditableTextWidget : EditableTextWidgetParent {
|
|||
|
||||
void addText(string txt) {
|
||||
version(custom_widgets) {
|
||||
|
||||
textLayout.addText(txt);
|
||||
|
||||
{
|
||||
|
@ -5579,7 +5606,7 @@ abstract class EditableTextWidget : EditableTextWidgetParent {
|
|||
SendMessageW( hwnd, EM_GETSEL, cast(WPARAM)(&StartPos), cast(LPARAM)(&EndPos) );
|
||||
|
||||
// move the caret to the end of the text
|
||||
int outLength = GetWindowTextLengthW( hwnd );
|
||||
int outLength = GetWindowTextLengthW(hwnd);
|
||||
SendMessageW( hwnd, EM_SETSEL, outLength, outLength );
|
||||
|
||||
// insert the text at the new caret position
|
||||
|
@ -6721,6 +6748,12 @@ struct icon { ushort id; }
|
|||
///
|
||||
/// Group: generating_from_code
|
||||
struct label { string label; }
|
||||
///
|
||||
/// Group: generating_from_code
|
||||
struct hotkey { dchar ch; }
|
||||
///
|
||||
/// Group: generating_from_code
|
||||
struct tip { string tip; }
|
||||
|
||||
|
||||
/++
|
||||
|
|
|
@ -11,7 +11,7 @@ static if(UsingWin32Widgets)
|
|||
/++
|
||||
|
||||
+/
|
||||
void showColorDialog(Window owner, Color current, void delegate(Color choice) onOK, void delegate() onCancel = null) {
|
||||
auto showColorDialog(Window owner, Color current, void delegate(Color choice) onOK, void delegate() onCancel = null) {
|
||||
static if(UsingWin32Widgets) {
|
||||
import core.sys.windows.windows;
|
||||
static COLORREF[16] customColors;
|
||||
|
@ -30,6 +30,7 @@ void showColorDialog(Window owner, Color current, void delegate(Color choice) on
|
|||
} else static if(UsingCustomWidgets) {
|
||||
auto cpd = new ColorPickerDialog(current, onOK, owner);
|
||||
cpd.show();
|
||||
return cpd;
|
||||
} else static assert(0);
|
||||
}
|
||||
|
||||
|
@ -53,7 +54,7 @@ class ColorPickerDialog : Dialog {
|
|||
void delegate(Color) onOK;
|
||||
|
||||
this(Color current, void delegate(Color) onOK, Window owner) {
|
||||
super(360, 350, "Color picker");
|
||||
super(360, 460, "Color picker");
|
||||
|
||||
this.onOK = onOK;
|
||||
|
||||
|
@ -79,7 +80,7 @@ class ColorPickerDialog : Dialog {
|
|||
canUseImage = true;
|
||||
|
||||
if(hslImage is null && canUseImage) {
|
||||
auto img = new TrueColorImage(180, 128);
|
||||
auto img = new TrueColorImage(360, 255);
|
||||
double h = 0.0, s = 1.0, l = 0.5;
|
||||
foreach(y; 0 .. img.height) {
|
||||
foreach(x; 0 .. img.width) {
|
||||
|
@ -99,60 +100,112 @@ class ColorPickerDialog : Dialog {
|
|||
this() { super(t); }
|
||||
override int minHeight() { return hslImage ? hslImage.height : 4; }
|
||||
override int maxHeight() { return hslImage ? hslImage.height : 4; }
|
||||
override int marginBottom() { return 4; }
|
||||
override void paint(ScreenPainter painter) {
|
||||
if(hslImage)
|
||||
hslImage.drawAt(painter, Point(0, 0));
|
||||
}
|
||||
};
|
||||
|
||||
auto hr = new HorizontalLayout(t);
|
||||
|
||||
auto vlRgb = new class VerticalLayout {
|
||||
this() {
|
||||
super(t);
|
||||
super(hr);
|
||||
}
|
||||
override int maxWidth() { return 150; };
|
||||
};
|
||||
|
||||
auto vlHsl = new class VerticalLayout {
|
||||
this() {
|
||||
super(hr);
|
||||
}
|
||||
override int maxWidth() { return 150; };
|
||||
};
|
||||
|
||||
h = new LabeledLineEdit("Hue:", vlHsl);
|
||||
s = new LabeledLineEdit("Saturation:", vlHsl);
|
||||
l = new LabeledLineEdit("Lightness:", vlHsl);
|
||||
|
||||
css = new LabeledLineEdit("CSS:", vlHsl);
|
||||
|
||||
r = new LabeledLineEdit("Red:", vlRgb);
|
||||
g = new LabeledLineEdit("Green:", vlRgb);
|
||||
b = new LabeledLineEdit("Blue:", vlRgb);
|
||||
a = new LabeledLineEdit("Alpha:", vlRgb);
|
||||
|
||||
import std.conv;
|
||||
import std.format;
|
||||
|
||||
r.content = to!string(current.r);
|
||||
g.content = to!string(current.g);
|
||||
b.content = to!string(current.b);
|
||||
a.content = to!string(current.a);
|
||||
void updateCurrent() {
|
||||
r.content = to!string(current.r);
|
||||
g.content = to!string(current.g);
|
||||
b.content = to!string(current.b);
|
||||
a.content = to!string(current.a);
|
||||
|
||||
auto hsl = current.toHsl;
|
||||
|
||||
h.content = format("%0.2f", hsl[0]);
|
||||
s.content = format("%0.2f", hsl[1]);
|
||||
l.content = format("%0.2f", hsl[2]);
|
||||
|
||||
css.content = current.toCssString();
|
||||
}
|
||||
|
||||
updateCurrent();
|
||||
|
||||
r.addEventListener("focus", &r.selectAll);
|
||||
g.addEventListener("focus", &g.selectAll);
|
||||
b.addEventListener("focus", &b.selectAll);
|
||||
a.addEventListener("focus", &a.selectAll);
|
||||
|
||||
h.addEventListener("focus", &h.selectAll);
|
||||
s.addEventListener("focus", &s.selectAll);
|
||||
l.addEventListener("focus", &l.selectAll);
|
||||
|
||||
if(hslImage !is null)
|
||||
wid.addEventListener("mousedown", (Event event) {
|
||||
auto h = cast(double) event.clientX / hslImage.width * 360.0;
|
||||
auto s = 1.0 - (cast(double) event.clientY / hslImage.height * 1.0);
|
||||
auto l = 0.5;
|
||||
css.addEventListener("focus", &css.selectAll);
|
||||
|
||||
auto color = Color.fromHsl(h, s, l);
|
||||
|
||||
r.content = to!string(color.r);
|
||||
g.content = to!string(color.g);
|
||||
b.content = to!string(color.b);
|
||||
a.content = to!string(color.a);
|
||||
|
||||
});
|
||||
|
||||
Color currentColor() {
|
||||
void convertFromHsl() {
|
||||
try {
|
||||
return Color(to!int(r.content), to!int(g.content), to!int(b.content), to!int(a.content));
|
||||
auto c = Color.fromHsl(h.content.to!double, s.content.to!double, l.content.to!double);
|
||||
c.a = a.content.to!ubyte;
|
||||
current = c;
|
||||
updateCurrent();
|
||||
} catch(Exception e) {
|
||||
return Color.transparent;
|
||||
}
|
||||
}
|
||||
|
||||
h.addEventListener("change", &convertFromHsl);
|
||||
s.addEventListener("change", &convertFromHsl);
|
||||
l.addEventListener("change", &convertFromHsl);
|
||||
|
||||
css.addEventListener("change", () {
|
||||
current = Color.fromString(css.content);
|
||||
updateCurrent();
|
||||
});
|
||||
|
||||
void helper(Event event) {
|
||||
auto h = cast(double) event.clientX / hslImage.width * 360.0;
|
||||
auto s = 1.0 - (cast(double) event.clientY / hslImage.height * 1.0);
|
||||
auto l = this.l.content.to!double;
|
||||
|
||||
current = Color.fromHsl(h, s, l);
|
||||
current.a = a.content.to!ubyte;
|
||||
|
||||
updateCurrent();
|
||||
|
||||
auto e2 = new Event("change", this);
|
||||
e2.dispatch();
|
||||
}
|
||||
|
||||
if(hslImage !is null)
|
||||
wid.addEventListener("mousedown", &helper);
|
||||
if(hslImage !is null)
|
||||
wid.addEventListener("mousemove", (Event event) {
|
||||
if(event.state & ModifierState.leftButtonDown)
|
||||
helper(event);
|
||||
});
|
||||
|
||||
this.addEventListener("keydown", (Event event) {
|
||||
if(event.key == Key.Enter || event.key == Key.PadEnter)
|
||||
OK();
|
||||
|
@ -205,6 +258,22 @@ class ColorPickerDialog : Dialog {
|
|||
LabeledLineEdit b;
|
||||
LabeledLineEdit a;
|
||||
|
||||
LabeledLineEdit h;
|
||||
LabeledLineEdit s;
|
||||
LabeledLineEdit l;
|
||||
|
||||
LabeledLineEdit css;
|
||||
|
||||
Color currentColor() {
|
||||
import std.conv;
|
||||
try {
|
||||
return Color(to!int(r.content), to!int(g.content), to!int(b.content), to!int(a.content));
|
||||
} catch(Exception e) {
|
||||
return Color.transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override void OK() {
|
||||
import std.conv;
|
||||
try {
|
||||
|
|
12
mssql.d
12
mssql.d
|
@ -155,16 +155,16 @@ class MsSqlResult : ResultSet {
|
|||
string a;
|
||||
|
||||
more:
|
||||
SQLCHAR[255] buf;
|
||||
if(SQLGetData(statement, cast(ushort)(i+1), SQL_CHAR, buf.ptr, 255, &ptr) != SQL_SUCCESS)
|
||||
SQLCHAR[1024] buf;
|
||||
if(SQLGetData(statement, cast(ushort)(i+1), SQL_CHAR, buf.ptr, 1024, &ptr) != SQL_SUCCESS)
|
||||
throw new DatabaseException("get data: " ~ getSQLError(SQL_HANDLE_STMT, statement));
|
||||
|
||||
assert(ptr != SQL_NO_TOTAL);
|
||||
if(ptr == SQL_NULL_DATA)
|
||||
a = null;
|
||||
else {
|
||||
a ~= cast(string) buf[0 .. ptr > 255 ? 255 : ptr].idup;
|
||||
ptr -= ptr > 255 ? 255 : ptr;
|
||||
a ~= cast(string) buf[0 .. ptr > 1024 ? 1024 : ptr].idup;
|
||||
ptr -= ptr > 1024 ? 1024 : ptr;
|
||||
if(ptr)
|
||||
goto more;
|
||||
}
|
||||
|
@ -181,11 +181,11 @@ class MsSqlResult : ResultSet {
|
|||
void makeFieldMapping() {
|
||||
for(int i = 0; i < numFields; i++) {
|
||||
SQLSMALLINT len;
|
||||
SQLCHAR[255] buf;
|
||||
SQLCHAR[1024] buf;
|
||||
auto ret = SQLDescribeCol(statement,
|
||||
cast(ushort)(i+1),
|
||||
cast(ubyte*)buf.ptr,
|
||||
255,
|
||||
1024,
|
||||
&len,
|
||||
null, null, null, null);
|
||||
if (ret != SQL_SUCCESS)
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
//
|
||||
// Would be nice: way to take output of the canvas to an image file (raster and/or svg)
|
||||
//
|
||||
//
|
||||
// Copyright (c) 2013 Mikko Mononen memon@inside.org
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
|
|
50
png.d
50
png.d
|
@ -22,6 +22,31 @@ void writePng(string filename, MemoryImage mi) {
|
|||
std.file.write(filename, writePng(png));
|
||||
}
|
||||
|
||||
///
|
||||
enum PngType {
|
||||
greyscale = 0, /// The data must be `depth` bits per pixel
|
||||
truecolor = 2, /// The data will be RGB triples, so `depth * 3` bits per pixel. Depth must be 8 or 16.
|
||||
indexed = 3, /// The data must be `depth` bits per pixel, with a palette attached. Use [writePng] with [IndexedImage] for this mode. Depth must be <= 8.
|
||||
greyscale_with_alpha = 4, /// The data must be (grey, alpha) byte pairs for each pixel. Thus `depth * 2` bits per pixel. Depth must be 8 or 16.
|
||||
truecolor_with_alpha = 6 /// The data must be RGBA quads for each pixel. Thus, `depth * 4` bits per pixel. Depth must be 8 or 16.
|
||||
}
|
||||
|
||||
/// Saves an image from an existing array. Note that depth other than 8 may not be implemented yet. Also note depth of 16 must be stored big endian
|
||||
void writePng(string filename, const ubyte[] data, int width, int height, PngType type, ubyte depth = 8) {
|
||||
PngHeader h;
|
||||
h.width = width;
|
||||
h.height = height;
|
||||
h.type = cast(ubyte) type;
|
||||
h.depth = depth;
|
||||
|
||||
auto png = blankPNG(h);
|
||||
addImageDatastreamToPng(data, png);
|
||||
|
||||
import std.file;
|
||||
std.file.write(filename, writePng(png));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
//Here's a simple test program that shows how to write a quick image viewer with simpledisplay:
|
||||
|
||||
|
@ -629,9 +654,28 @@ void addImageDatastreamToPng(const(ubyte)[] data, PNG* png) {
|
|||
|
||||
PngHeader h = getHeader(png);
|
||||
|
||||
auto bytesPerLine = h.width * 4;
|
||||
if(h.type == 3)
|
||||
bytesPerLine = h.width * h.depth / 8;
|
||||
int bytesPerLine;
|
||||
switch(h.type) {
|
||||
case 0:
|
||||
// FIXME: < 8 depth not supported here but should be
|
||||
bytesPerLine = h.width * 1 * h.depth / 8;
|
||||
break;
|
||||
case 2:
|
||||
bytesPerLine = h.width * 3 * h.depth / 8;
|
||||
break;
|
||||
case 3:
|
||||
bytesPerLine = h.width * 1 * h.depth / 8;
|
||||
break;
|
||||
case 4:
|
||||
// FIXME: < 8 depth not supported here but should be
|
||||
bytesPerLine = h.width * 2 * h.depth / 8;
|
||||
break;
|
||||
case 6:
|
||||
bytesPerLine = h.width * 4 * h.depth / 8;
|
||||
break;
|
||||
default: assert(0);
|
||||
|
||||
}
|
||||
Chunk dat;
|
||||
dat.type = ['I', 'D', 'A', 'T'];
|
||||
int pos = 0;
|
||||
|
|
8
script.d
8
script.d
|
@ -90,6 +90,8 @@
|
|||
obj.__prop("name", value); // bypasses operator overloading, useful for use inside the opIndexAssign especially
|
||||
|
||||
Note: if opIndex is not overloaded, getting a non-existent member will actually add it to the member. This might be a bug but is needed right now in the D impl for nice chaining. Or is it? FIXME
|
||||
|
||||
FIXME: it doesn't do opIndex with multiple args.
|
||||
* if/else
|
||||
* array slicing, but note that slices are rvalues currently
|
||||
* variables must start with A-Z, a-z, _, or $, then must be [A-Za-z0-9_]*.
|
||||
|
@ -2304,7 +2306,9 @@ Expression parseExpression(MyTokenStreamHere)(ref MyTokenStreamHere tokens, bool
|
|||
auto ident = tokens.requireNextToken(ScriptToken.Type.identifier);
|
||||
|
||||
tokens.requireNextToken(ScriptToken.Type.symbol, "(");
|
||||
auto args = parseVariableDeclaration(tokens, ")");
|
||||
VariableDeclaration args;
|
||||
if(!tokens.peekNextToken(ScriptToken.Type.symbol, ")"))
|
||||
args = parseVariableDeclaration(tokens, ")");
|
||||
tokens.requireNextToken(ScriptToken.Type.symbol, ")");
|
||||
auto bod = parseExpression(tokens);
|
||||
|
||||
|
@ -2538,7 +2542,7 @@ Expression parseStatement(MyTokenStreamHere)(ref MyTokenStreamHere tokens, strin
|
|||
|
||||
return parseFunctionCall(tokens, new AssertKeyword(token));
|
||||
|
||||
break;
|
||||
//break;
|
||||
// declarations
|
||||
case "var":
|
||||
return parseVariableDeclaration(tokens, ";");
|
||||
|
|
139
simpledisplay.d
139
simpledisplay.d
|
@ -1158,6 +1158,64 @@ string sdpyWindowClass () {
|
|||
return null;
|
||||
}
|
||||
|
||||
/++
|
||||
Returns the DPI of the default monitor. [0] is width, [1] is height (they are usually the same though). You may wish to round the numbers off.
|
||||
+/
|
||||
float[2] getDpi() {
|
||||
float[2] dpi;
|
||||
version(Windows) {
|
||||
HDC screen = GetDC(null);
|
||||
dpi[0] = GetDeviceCaps(screen, LOGPIXELSX);
|
||||
dpi[1] = GetDeviceCaps(screen, LOGPIXELSY);
|
||||
} else version(X11) {
|
||||
auto display = XDisplayConnection.get;
|
||||
auto screen = DefaultScreen(display);
|
||||
|
||||
void fallback() {
|
||||
// 25.4 millimeters in an inch...
|
||||
dpi[0] = cast(float) DisplayWidth(display, screen) / DisplayWidthMM(display, screen) * 25.4;
|
||||
dpi[1] = cast(float) DisplayHeight(display, screen) / DisplayHeightMM(display, screen) * 25.4;
|
||||
}
|
||||
|
||||
char* resourceString = XResourceManagerString(display);
|
||||
XrmInitialize();
|
||||
|
||||
auto db = XrmGetStringDatabase(resourceString);
|
||||
|
||||
if (resourceString) {
|
||||
XrmValue value;
|
||||
char* type;
|
||||
if (XrmGetResource(db, "Xft.dpi", "String", &type, &value) == true) {
|
||||
if (value.addr) {
|
||||
import core.stdc.stdlib;
|
||||
dpi[0] = atof(cast(char*) value.addr);
|
||||
dpi[1] = dpi[0];
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
} else {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
|
||||
return dpi;
|
||||
}
|
||||
|
||||
version(X11) {
|
||||
extern(C) char* XResourceManagerString(Display*);
|
||||
extern(C) void XrmInitialize();
|
||||
extern(C) XrmDatabase XrmGetStringDatabase(char* data);
|
||||
extern(C) bool XrmGetResource(XrmDatabase, const char*, const char*, char**, XrmValue*);
|
||||
alias XrmDatabase = void*;
|
||||
struct XrmValue {
|
||||
uint size;
|
||||
void* addr;
|
||||
}
|
||||
}
|
||||
|
||||
TrueColorImage trueColorImageFromNativeHandle(NativeWindowHandle handle, int width, int height) {
|
||||
throw new Exception("not implemented");
|
||||
version(none) {
|
||||
|
@ -1165,6 +1223,8 @@ TrueColorImage trueColorImageFromNativeHandle(NativeWindowHandle handle, int wid
|
|||
auto display = XDisplayConnection.get;
|
||||
auto image = XGetImage(display, handle, 0, 0, width, height, (cast(c_ulong) ~0) /*AllPlanes*/, ZPixmap);
|
||||
|
||||
// https://github.com/adamdruppe/arsd/issues/98
|
||||
|
||||
// FIXME: copy that shit
|
||||
|
||||
XDestroyImage(image);
|
||||
|
@ -1990,11 +2050,15 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
|
|||
|
||||
+/
|
||||
@property void cursor(MouseCursor cursor) {
|
||||
auto ch = cursor.cursorHandle;
|
||||
version(OSXCocoa)
|
||||
featureNotImplemented();
|
||||
else
|
||||
if(this.impl.curHidden <= 0) {
|
||||
static if(UsingSimpledisplayX11) {
|
||||
auto ch = cursor.cursorHandle;
|
||||
XDefineCursor(XDisplayConnection.get(), this.impl.window, ch);
|
||||
} else version(Windows) {
|
||||
auto ch = cursor.cursorHandle;
|
||||
impl.currentCursor = ch;
|
||||
SetCursor(ch); // redraw without waiting for mouse movement to update
|
||||
} else featureNotImplemented();
|
||||
|
@ -2602,10 +2666,13 @@ static struct GenericCursor {
|
|||
struct EventLoop {
|
||||
@disable this();
|
||||
|
||||
/// Gets a reference to an existing event loop
|
||||
static EventLoop get() {
|
||||
return EventLoop(0, null);
|
||||
}
|
||||
|
||||
/// Construct an application-global event loop for yourself
|
||||
/// See_Also: [SimpleWindow.setEventHandlers]
|
||||
this(long pulseTimeout, void delegate() handlePulse) {
|
||||
if(impl is null)
|
||||
impl = new EventLoopImpl(pulseTimeout, handlePulse);
|
||||
|
@ -2633,12 +2700,14 @@ struct EventLoop {
|
|||
impl.refcount++;
|
||||
}
|
||||
|
||||
/// Runs the event loop until the whileCondition, if present, returns false
|
||||
int run(bool delegate() whileCondition = null) {
|
||||
assert(impl !is null);
|
||||
impl.notExited = true;
|
||||
return impl.run(whileCondition);
|
||||
}
|
||||
|
||||
/// Exits the event loop
|
||||
void exit() {
|
||||
assert(impl !is null);
|
||||
impl.notExited = false;
|
||||
|
@ -11895,6 +11964,8 @@ struct Visual
|
|||
int DefaultDepth(Display* dpy, int scr) { return ScreenOfDisplay(dpy, scr).root_depth; }
|
||||
int DisplayWidth(Display* dpy, int scr) { return ScreenOfDisplay(dpy, scr).width; }
|
||||
int DisplayHeight(Display* dpy, int scr) { return ScreenOfDisplay(dpy, scr).height; }
|
||||
int DisplayWidthMM(Display* dpy, int scr) { return ScreenOfDisplay(dpy, scr).mwidth; }
|
||||
int DisplayHeightMM(Display* dpy, int scr) { return ScreenOfDisplay(dpy, scr).mheight; }
|
||||
auto DefaultColormap(Display* dpy, int scr) { return ScreenOfDisplay(dpy, scr).cmap; }
|
||||
|
||||
int ConnectionNumber(Display* dpy) { return dpy.fd; }
|
||||
|
@ -12189,7 +12260,7 @@ private:
|
|||
alias const(void)* CGContextRef;
|
||||
alias const(void)* CGColorSpaceRef;
|
||||
alias const(void)* CGImageRef;
|
||||
alias uint CGBitmapInfo;
|
||||
alias ulong CGBitmapInfo;
|
||||
|
||||
struct objc_super {
|
||||
id self;
|
||||
|
@ -12197,18 +12268,18 @@ private:
|
|||
}
|
||||
|
||||
struct CFRange {
|
||||
int location, length;
|
||||
long location, length;
|
||||
}
|
||||
|
||||
struct NSPoint {
|
||||
float x, y;
|
||||
double x, y;
|
||||
|
||||
static fromTuple(T)(T tupl) {
|
||||
return NSPoint(tupl.tupleof);
|
||||
}
|
||||
}
|
||||
struct NSSize {
|
||||
float width, height;
|
||||
double width, height;
|
||||
}
|
||||
struct NSRect {
|
||||
NSPoint origin;
|
||||
|
@ -12219,7 +12290,7 @@ private:
|
|||
alias NSRect CGRect;
|
||||
|
||||
struct CGAffineTransform {
|
||||
float a, b, c, d, tx, ty;
|
||||
double a, b, c, d, tx, ty;
|
||||
}
|
||||
|
||||
enum NSApplicationActivationPolicyRegular = 0;
|
||||
|
@ -12235,7 +12306,7 @@ private:
|
|||
NSTexturedBackgroundWindowMask = 1 << 8
|
||||
}
|
||||
|
||||
enum : uint {
|
||||
enum : ulong {
|
||||
kCGImageAlphaNone,
|
||||
kCGImageAlphaPremultipliedLast,
|
||||
kCGImageAlphaPremultipliedFirst,
|
||||
|
@ -12244,7 +12315,7 @@ private:
|
|||
kCGImageAlphaNoneSkipLast,
|
||||
kCGImageAlphaNoneSkipFirst
|
||||
}
|
||||
enum : uint {
|
||||
enum : ulong {
|
||||
kCGBitmapAlphaInfoMask = 0x1F,
|
||||
kCGBitmapFloatComponents = (1 << 8),
|
||||
kCGBitmapByteOrderMask = 0x7000,
|
||||
|
@ -12293,12 +12364,12 @@ private:
|
|||
|
||||
CFStringRef CFStringCreateWithBytes(CFAllocatorRef allocator,
|
||||
const(char)* bytes, long numBytes,
|
||||
int encoding,
|
||||
long encoding,
|
||||
BOOL isExternalRepresentation);
|
||||
int CFStringGetBytes(CFStringRef theString, CFRange range, int encoding,
|
||||
long CFStringGetBytes(CFStringRef theString, CFRange range, long encoding,
|
||||
char lossByte, bool isExternalRepresentation,
|
||||
char* buffer, long maxBufLen, long* usedBufLen);
|
||||
int CFStringGetLength(CFStringRef theString);
|
||||
long CFStringGetLength(CFStringRef theString);
|
||||
|
||||
CGContextRef CGBitmapContextCreate(void* data,
|
||||
size_t width, size_t height,
|
||||
|
@ -12316,13 +12387,13 @@ private:
|
|||
void CGColorSpaceRelease(CGColorSpaceRef cs);
|
||||
|
||||
void CGContextSetRGBStrokeColor(CGContextRef c,
|
||||
float red, float green, float blue,
|
||||
float alpha);
|
||||
double red, double green, double blue,
|
||||
double alpha);
|
||||
void CGContextSetRGBFillColor(CGContextRef c,
|
||||
float red, float green, float blue,
|
||||
float alpha);
|
||||
double red, double green, double blue,
|
||||
double alpha);
|
||||
void CGContextDrawImage(CGContextRef c, CGRect rect, CGImageRef image);
|
||||
void CGContextShowTextAtPoint(CGContextRef c, float x, float y,
|
||||
void CGContextShowTextAtPoint(CGContextRef c, double x, double y,
|
||||
const(char)* str, size_t length);
|
||||
void CGContextStrokeLineSegments(CGContextRef c,
|
||||
const(CGPoint)* points, size_t count);
|
||||
|
@ -12330,15 +12401,15 @@ private:
|
|||
void CGContextBeginPath(CGContextRef c);
|
||||
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode);
|
||||
void CGContextAddEllipseInRect(CGContextRef c, CGRect rect);
|
||||
void CGContextAddArc(CGContextRef c, float x, float y, float radius,
|
||||
float startAngle, float endAngle, int clockwise);
|
||||
void CGContextAddArc(CGContextRef c, double x, double y, double radius,
|
||||
double startAngle, double endAngle, long clockwise);
|
||||
void CGContextAddRect(CGContextRef c, CGRect rect);
|
||||
void CGContextAddLines(CGContextRef c,
|
||||
const(CGPoint)* points, size_t count);
|
||||
void CGContextSaveGState(CGContextRef c);
|
||||
void CGContextRestoreGState(CGContextRef c);
|
||||
void CGContextSelectFont(CGContextRef c, const(char)* name, float size,
|
||||
uint textEncoding);
|
||||
void CGContextSelectFont(CGContextRef c, const(char)* name, double size,
|
||||
ulong textEncoding);
|
||||
CGAffineTransform CGContextGetTextMatrix(CGContextRef c);
|
||||
void CGContextSetTextMatrix(CGContextRef c, CGAffineTransform t);
|
||||
|
||||
|
@ -12348,7 +12419,7 @@ private:
|
|||
private:
|
||||
// A convenient method to create a CFString (=NSString) from a D string.
|
||||
CFStringRef createCFString(string str) {
|
||||
return CFStringCreateWithBytes(null, str.ptr, cast(int) str.length,
|
||||
return CFStringCreateWithBytes(null, str.ptr, cast(long) str.length,
|
||||
kCFStringEncodingUTF8, false);
|
||||
}
|
||||
|
||||
|
@ -12435,7 +12506,7 @@ version(OSXCocoa) {
|
|||
|
||||
// if rawData had a length....
|
||||
//assert(rawData.length == where.length);
|
||||
for(int idx = 0; idx < where.length; idx += 4) {
|
||||
for(long idx = 0; idx < where.length; idx += 4) {
|
||||
auto alpha = rawData[idx + 3];
|
||||
if(alpha == 255) {
|
||||
where[idx + 0] = rawData[idx + 0]; // r
|
||||
|
@ -12458,7 +12529,7 @@ version(OSXCocoa) {
|
|||
|
||||
// if rawData had a length....
|
||||
//assert(rawData.length == where.length);
|
||||
for(int idx = 0; idx < where.length; idx += 4) {
|
||||
for(long idx = 0; idx < where.length; idx += 4) {
|
||||
auto alpha = rawData[idx + 3];
|
||||
if(alpha == 255) {
|
||||
rawData[idx + 0] = where[idx + 0]; // r
|
||||
|
@ -12508,17 +12579,19 @@ version(OSXCocoa) {
|
|||
mixin template NativeScreenPainterImplementation() {
|
||||
CGContextRef context;
|
||||
ubyte[4] _outlineComponents;
|
||||
id view;
|
||||
|
||||
void create(NativeWindowHandle window) {
|
||||
context = window.drawingContext;
|
||||
view = window.view;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
setNeedsDisplay(view, true);
|
||||
}
|
||||
|
||||
// NotYetImplementedException
|
||||
Size textSize(in char[] txt) { return Size(32, 16); throw new NotYetImplementedException(); }
|
||||
void pen(Pen p) {}
|
||||
void rasterOp(RasterOp op) {}
|
||||
Pen _activePen;
|
||||
Color _fillColor;
|
||||
|
@ -12529,8 +12602,10 @@ version(OSXCocoa) {
|
|||
|
||||
// end
|
||||
|
||||
@property void outlineColor(Color color) {
|
||||
float alphaComponent = color.a/255.0f;
|
||||
void pen(Pen pen) {
|
||||
_activePen = pen;
|
||||
auto color = pen.color; // FIXME
|
||||
double alphaComponent = color.a/255.0f;
|
||||
CGContextSetRGBStrokeColor(context,
|
||||
color.r/255.0f, color.g/255.0f, color.b/255.0f, alphaComponent);
|
||||
|
||||
|
@ -12581,7 +12656,7 @@ version(OSXCocoa) {
|
|||
_outlineComponents[1]*invAlpha,
|
||||
_outlineComponents[2]*invAlpha,
|
||||
_outlineComponents[3]/255.0f);
|
||||
CGContextShowTextAtPoint(context, x, y, text.ptr, text.length);
|
||||
CGContextShowTextAtPoint(context, x, y + 12 /* this is cuz this picks baseline but i want bounding box */, text.ptr, text.length);
|
||||
// auto cfstr = cast(id)createCFString(text);
|
||||
// objc_msgSend(cfstr, sel_registerName("drawAtPoint:withAttributes:"),
|
||||
// NSPoint(x, y), null);
|
||||
|
@ -12748,9 +12823,13 @@ version(OSXCocoa) {
|
|||
// will like it. Let's leave it to the native handler.
|
||||
|
||||
// perform the default action.
|
||||
auto superData = objc_super(self, superclass(self));
|
||||
alias extern(C) void function(objc_super*, SEL, id) T;
|
||||
(cast(T)&objc_msgSendSuper)(&superData, _cmd, event);
|
||||
|
||||
// so the default action is to make a bomp sound and i dont want that
|
||||
// sooooooooo yeah not gonna do that.
|
||||
|
||||
//auto superData = objc_super(self, superclass(self));
|
||||
//alias extern(C) void function(objc_super*, SEL, id) T;
|
||||
//(cast(T)&objc_msgSendSuper)(&superData, _cmd, event);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
138
terminal.d
138
terminal.d
|
@ -1,4 +1,3 @@
|
|||
// FIXME: have a simple function that integrates with sdpy event loop. it can be a template
|
||||
// for optional dependency
|
||||
/++
|
||||
Module for interacting with the user's terminal, including color output, cursor manipulation, and full-featured real-time mouse and keyboard input. Also includes high-level convenience methods, like [Terminal.getline], which gives the user a line editor with history, completion, etc. See the [#examples].
|
||||
|
@ -68,7 +67,7 @@ module arsd.terminal;
|
|||
|
||||
This example will demonstrate the high-level getline interface.
|
||||
|
||||
The user will be able to type a line and navigate around it with cursor keys and even the mouse on some systems, as well as perform editing as they expect (e.g. the backspace and delete keys work normally) until they press enter. Then, the final line will be returned to your program, which the example will simply print back to the user.
|
||||
The user will be able to type a line and navigate around it with cursor keys and even the mouse on some systems, as well as perform editing as they expect (e.g. the backspace and delete keys work normally) until they press enter. Then, the final line will be returned to your program, which the example will simply print back to the user.
|
||||
+/
|
||||
unittest {
|
||||
import arsd.terminal;
|
||||
|
@ -78,6 +77,8 @@ unittest {
|
|||
string line = terminal.getline();
|
||||
terminal.writeln("You wrote: ", line);
|
||||
}
|
||||
|
||||
main; // exclude from docs
|
||||
}
|
||||
|
||||
/++
|
||||
|
@ -88,6 +89,7 @@ unittest {
|
|||
+/
|
||||
unittest {
|
||||
import arsd.terminal;
|
||||
|
||||
void main() {
|
||||
auto terminal = Terminal(ConsoleOutputType.linear);
|
||||
terminal.color(Color.green, Color.black);
|
||||
|
@ -95,6 +97,8 @@ unittest {
|
|||
terminal.color(Color.DEFAULT, Color.DEFAULT);
|
||||
terminal.writeln("And back to normal.");
|
||||
}
|
||||
|
||||
main; // exclude from docs
|
||||
}
|
||||
|
||||
/++
|
||||
|
@ -105,6 +109,7 @@ unittest {
|
|||
+/
|
||||
unittest {
|
||||
import arsd.terminal;
|
||||
|
||||
void main() {
|
||||
auto terminal = Terminal(ConsoleOutputType.linear);
|
||||
auto input = RealTimeConsoleInput(&terminal, ConsoleInputFlags.raw);
|
||||
|
@ -113,6 +118,8 @@ unittest {
|
|||
auto ch = input.getch();
|
||||
terminal.writeln("You pressed ", ch);
|
||||
}
|
||||
|
||||
main; // exclude from docs
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -427,6 +434,25 @@ struct Terminal {
|
|||
@disable this(this);
|
||||
private ConsoleOutputType type;
|
||||
|
||||
/++
|
||||
Terminal is only valid to use on an actual console device or terminal
|
||||
handle. You should not attempt to construct a Terminal instance if this
|
||||
returns false;
|
||||
+/
|
||||
static bool stdoutIsTerminal() {
|
||||
version(Posix) {
|
||||
import core.sys.posix.unistd;
|
||||
return cast(bool) isatty(1);
|
||||
} else version(Windows) {
|
||||
auto hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO originalSbi;
|
||||
if(GetConsoleScreenBufferInfo(hConsole, &originalSbi) == 0)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
} else static assert(0);
|
||||
}
|
||||
|
||||
version(Posix) {
|
||||
private int fdOut;
|
||||
private int fdIn;
|
||||
|
@ -797,11 +823,19 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
|||
|
||||
defaultForegroundColor = cast(Color) (originalSbi.wAttributes & 0x0f);
|
||||
defaultBackgroundColor = cast(Color) ((originalSbi.wAttributes >> 4) & 0x0f);
|
||||
|
||||
oldCp = GetConsoleOutputCP();
|
||||
SetConsoleOutputCP(65001); // UTF-8
|
||||
|
||||
oldCpIn = GetConsoleCP();
|
||||
SetConsoleCP(65001); // UTF-8
|
||||
}
|
||||
|
||||
version(Windows) {
|
||||
private Color defaultBackgroundColor = Color.black;
|
||||
private Color defaultForegroundColor = Color.white;
|
||||
UINT oldCp;
|
||||
UINT oldCpIn;
|
||||
}
|
||||
|
||||
// only use this if you are sure you know what you want, since the terminal is a shared resource you generally really want to reset it to normal when you leave...
|
||||
|
@ -836,6 +870,10 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
|||
if(lineGetter !is null)
|
||||
lineGetter.dispose();
|
||||
|
||||
|
||||
SetConsoleOutputCP(oldCp);
|
||||
SetConsoleCP(oldCpIn);
|
||||
|
||||
auto stdo = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleActiveScreenBuffer(stdo);
|
||||
if(hConsole !is stdo)
|
||||
|
@ -1587,6 +1625,16 @@ struct RealTimeConsoleInput {
|
|||
|
||||
void delegate(InputEvent) userEventHandler;
|
||||
|
||||
/++
|
||||
If you are using [arsd.simpledisplay] and want terminal interop too, you can call
|
||||
this function to add it to the sdpy event loop and get the callback called on new
|
||||
input.
|
||||
|
||||
Note that you will probably need to call `terminal.flush()` when you are doing doing
|
||||
output, as the sdpy event loop doesn't know to do that (yet). I will probably change
|
||||
that in a future version, but it doesn't hurt to call it twice anyway, so I recommend
|
||||
calling flush yourself in any code you write using this.
|
||||
+/
|
||||
void integrateWithSimpleDisplayEventLoop()(void delegate(InputEvent) userEventHandler) {
|
||||
this.userEventHandler = userEventHandler;
|
||||
import arsd.simpledisplay;
|
||||
|
@ -3577,7 +3625,7 @@ struct ScrollbackBuffer {
|
|||
}
|
||||
|
||||
void clear() {
|
||||
lines = null;
|
||||
lines.clear();
|
||||
clickRegions = null;
|
||||
scrollbackPosition = 0;
|
||||
}
|
||||
|
@ -3679,9 +3727,87 @@ struct ScrollbackBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME: limit scrollback lines.length
|
||||
static struct CircularBuffer(T) {
|
||||
T[] backing;
|
||||
|
||||
Line[] lines;
|
||||
enum maxScrollback = 8192; // as a power of 2, i hope the compiler optimizes the % below to a simple bit mask...
|
||||
|
||||
int start;
|
||||
int length_;
|
||||
|
||||
void clear() {
|
||||
backing = null;
|
||||
start = 0;
|
||||
length_ = 0;
|
||||
}
|
||||
|
||||
size_t length() {
|
||||
return length_;
|
||||
}
|
||||
|
||||
void opOpAssign(string op : "~")(T line) {
|
||||
if(length_ < maxScrollback) {
|
||||
backing.assumeSafeAppend();
|
||||
backing ~= line;
|
||||
length_++;
|
||||
} else {
|
||||
backing[start] = line;
|
||||
start++;
|
||||
if(start == maxScrollback)
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
T opIndex(int idx) {
|
||||
return backing[(start + idx) % maxScrollback];
|
||||
}
|
||||
T opIndex(Dollar idx) {
|
||||
return backing[(start + (length + idx.offsetFromEnd)) % maxScrollback];
|
||||
}
|
||||
|
||||
CircularBufferRange opSlice(int startOfIteration, Dollar end) {
|
||||
return CircularBufferRange(&this, startOfIteration, cast(int) length - startOfIteration + end.offsetFromEnd);
|
||||
}
|
||||
CircularBufferRange opSlice(int startOfIteration, int end) {
|
||||
return CircularBufferRange(&this, startOfIteration, end - startOfIteration);
|
||||
}
|
||||
CircularBufferRange opSlice() {
|
||||
return CircularBufferRange(&this, 0, cast(int) length);
|
||||
}
|
||||
|
||||
static struct CircularBufferRange {
|
||||
CircularBuffer* item;
|
||||
int position;
|
||||
int remaining;
|
||||
this(CircularBuffer* item, int startOfIteration, int count) {
|
||||
this.item = item;
|
||||
position = startOfIteration;
|
||||
remaining = count;
|
||||
}
|
||||
|
||||
T front() { return (*item)[position]; }
|
||||
bool empty() { return remaining <= 0; }
|
||||
void popFront() {
|
||||
position++;
|
||||
remaining--;
|
||||
}
|
||||
|
||||
T back() { return (*item)[remaining - 1 - position]; }
|
||||
void popBack() {
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
|
||||
static struct Dollar {
|
||||
int offsetFromEnd;
|
||||
Dollar opBinary(string op : "-")(int rhs) {
|
||||
return Dollar(offsetFromEnd - rhs);
|
||||
}
|
||||
}
|
||||
Dollar opDollar() { return Dollar(0); }
|
||||
}
|
||||
|
||||
CircularBuffer!Line lines;
|
||||
string name;
|
||||
|
||||
int x, y, width, height;
|
||||
|
@ -3811,7 +3937,7 @@ struct ScrollbackBuffer {
|
|||
// second pass: actually draw it
|
||||
int linePos = remaining;
|
||||
|
||||
foreach(idx, line; lines[start .. start + howMany]) {
|
||||
foreach(line; lines[start .. start + howMany]) {
|
||||
int written = 0;
|
||||
|
||||
if(linePos < 0) {
|
||||
|
|
Loading…
Reference in New Issue