Merge pull request #17 from hdon/master

Fix some crashes, OpenGL context sharing, and other minor changes
This commit is contained in:
Vadim Lopatin 2014-09-02 10:53:11 +04:00
commit b26673372d
11 changed files with 352 additions and 258 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ bin
*.obj *.obj
*.~* *.~*
*.*~ *.*~
.*.sw*

View File

@ -36,156 +36,151 @@ Copyright: Vadim Lopatin, 2014
License: Boost License 1.0 License: Boost License 1.0
Authors: Vadim Lopatin, coolreader.org@gmail.com Authors: Vadim Lopatin, coolreader.org@gmail.com
*/ */
module dlangui.core.i18n; module dlangui.core.i18n;
import dlangui.core.types; import dlangui.core.types;
import dlangui.core.logger; import dlangui.core.logger;
import std.utf; import std.utf;
/// container for UI string - either raw value or string resource ID /// container for UI string - either raw value or string resource ID
struct UIString { struct UIString {
/// if not null, use it, otherwise lookup by id /// if not null, use it, otherwise lookup by id
private dstring _value; private dstring _value;
/// id to find value in translator /// id to find value in translator
private string _id; private string _id;
/// create string with i18n resource id /// create string with i18n resource id
this(string id) { this(string id) {
_id = id; _id = id;
} }
/// create string with raw value /// create string with raw value
this(dstring value) { this(dstring value) {
_value = value; _value = value;
} }
@property string id() const { return _id; } @property string id() const { return _id; }
@property void id(string ID) { @property void id(string ID) {
_id = ID; _id = ID;
_value = null; _value = null;
} }
/// get value (either raw or translated by id) /// get value (either raw or translated by id)
@property dstring value() const { @property dstring value() const {
if (_value !is null) if (_value !is null)
return _value; return _value;
if (_id is null) if (_id is null)
return null; return null;
// translate ID to dstring // translate ID to dstring
return i18n.get(_id); return i18n.get(_id);
} }
/// set raw value /// set raw value
@property void value(dstring newValue) { @property void value(dstring newValue) {
_value = newValue; _value = newValue;
} }
/// assign raw value /// assign raw value
ref UIString opAssign(dstring rawValue) { ref UIString opAssign(dstring rawValue) {
_value = rawValue; _value = rawValue;
_id = null; _id = null;
return this; return this;
} }
/// assign ID /// assign ID
ref UIString opAssign(string ID) { ref UIString opAssign(string ID) {
_id = ID; _id = ID;
_value = null; _value = null;
return this; return this;
} }
/// default conversion to dstring /// default conversion to dstring
alias value this; alias value this;
} }
public __gshared UIStringTranslator i18n = new UIStringTranslator(); shared UIStringTranslator i18n;
//static shared this() { shared static this() {
// i18n = new UIStringTranslator(); i18n = new shared UIStringTranslator();
//} }
class UIStringTranslator { synchronized class UIStringTranslator {
private UIStringList _main; private UIStringList _main;
private UIStringList _fallback; private UIStringList _fallback;
private string[] _resourceDirs; private string[] _resourceDirs;
/// get i18n resource directory /// looks for i18n directory inside one of passed dirs, and uses first found as directory to read i18n files from
@property string[] resourceDirs() { return _resourceDirs; } void findTranslationsDir(string[] dirs ...) {
/// set i18n resource directory _resourceDirs.length = 0;
@property void resourceDirs(string[] dirs) { _resourceDirs = dirs; } import std.file;
/// looks for i18n directory inside one of passed dirs, and uses first found as directory to read i18n files from foreach(dir; dirs) {
string[] findTranslationsDir(string[] dirs ...) { string path = appendPath(dir, "i18n/");
_resourceDirs.length = 0;
import std.file;
foreach(dir; dirs) {
string path = appendPath(dir, "i18n/");
if (exists(path) && isDir(path)) { if (exists(path) && isDir(path)) {
Log.i("Adding i18n dir ", path); Log.i("Adding i18n dir ", path);
_resourceDirs ~= path; _resourceDirs ~= path;
} }
} }
return _resourceDirs; }
}
/// convert resource path - append resource dir if necessary
/// convert resource path - append resource dir if necessary string[] convertResourcePaths(string filename) {
string[] convertResourcePaths(string filename) { if (filename is null)
if (filename is null) return null;
return null; bool hasPathDelimiters = false;
bool hasPathDelimiters = false; foreach(char ch; filename)
foreach(char ch; filename) if (ch == '/' || ch == '\\')
if (ch == '/' || ch == '\\') hasPathDelimiters = true;
hasPathDelimiters = true; string[] res;
string[] res; if (!hasPathDelimiters && _resourceDirs.length) {
if (!hasPathDelimiters && _resourceDirs.length) { foreach (dir; _resourceDirs)
foreach (dir; _resourceDirs) res ~= dir ~ filename;
res ~= dir ~ filename; } else {
} else { res ~= filename;
res ~= filename; }
} return res;
return res; }
}
this() {
this() { _main = new shared UIStringList();
_main = new UIStringList(); _fallback = new shared UIStringList();
_fallback = new UIStringList(); }
} /// load translation file(s)
/// load translation file(s) bool load(string mainFilename, string fallbackFilename = null) {
bool load(string mainFilename, string fallbackFilename = null) { _main.clear();
_main.clear(); _fallback.clear();
_fallback.clear(); bool res = _main.load(convertResourcePaths(mainFilename));
bool res = _main.load(convertResourcePaths(mainFilename)); if (fallbackFilename !is null) {
if (fallbackFilename !is null) { res = _fallback.load(convertResourcePaths(fallbackFilename)) || res;
res = _fallback.load(convertResourcePaths(fallbackFilename)) || res; }
} return res;
return res; }
} /// translate string ID to string (returns "UNTRANSLATED: id" for missing values)
/// translate string ID to string (returns "UNTRANSLATED: id" for missing values) dstring get(string id) {
dstring get(string id) { if (id is null)
if (id is null) return null;
return null; dstring s = _main.get(id);
dstring s = _main.get(id); if (s !is null)
if (s !is null) return s;
return s; s = _fallback.get(id);
s = _fallback.get(id); if (s !is null)
if (s !is null) return s;
return s; return "UNTRANSLATED: "d ~ toUTF32(id);
return "UNTRANSLATED: "d ~ toUTF32(id); }
} }
}
/// UI string translator
/// UI string translator private shared class UIStringList {
class UIStringList { private dstring[string] _map;
private dstring[string] _map; /// remove all items
/// remove all items void clear() {
void clear() { _map.clear();
_map.clear(); }
} /// set item value
/// set item value void set(string id, dstring value) {
void set(string id, dstring value) { _map[id] = value;
_map[id] = value; }
} /// get item value, null if translation is not found for id
/// get item value, null if translation is not found for id dstring get(string id) const {
dstring get(string id) const { if (id in _map)
if (id in _map) return _map[id];
return _map[id]; return null;
return null; }
} /// load strings from stream
/// load strings from stream bool load(std.stream.InputStream stream) {
bool load(std.stream.InputStream stream) {
dlangui.core.linestream.LineStream lines = dlangui.core.linestream.LineStream.create(stream, ""); dlangui.core.linestream.LineStream lines = dlangui.core.linestream.LineStream.create(stream, "");
int count = 0; int count = 0;
for (;;) { for (;;) {
@ -212,21 +207,21 @@ class UIStringList {
} }
} }
return count > 0; return count > 0;
} }
/// load strings from file (utf8, id=value lines) /// load strings from file (utf8, id=value lines)
bool load(string[] filenames) { bool load(string[] filenames) {
clear(); clear();
bool res = false; bool res = false;
foreach(filename; filenames) { foreach(filename; filenames) {
import std.stream; import std.stream;
import std.file; import std.file;
try { try {
debug Log.d("Loading string resources from file ", filename); debug Log.d("Loading string resources from file ", filename);
if (!exists(filename) || !isFile(filename)) { if (!exists(filename) || !isFile(filename)) {
Log.e("File does not exist: ", filename); Log.e("File does not exist: ", filename);
continue; continue;
} }
std.stream.File f = new std.stream.File(filename); std.stream.File f = new std.stream.File(filename);
scope(exit) { f.close(); } scope(exit) { f.close(); }
res = load(f) || res; res = load(f) || res;
@ -235,5 +230,5 @@ class UIStringList {
} }
} }
return res; return res;
} }
} }

View File

@ -39,71 +39,73 @@ enum LogLevel : int {
Trace Trace
} }
__gshared LogLevel logLevel = LogLevel.Info;
__gshared std.stdio.File logFile;
void setLogLevel(LogLevel level) {
logLevel = level;
}
long currentTimeMillis() { long currentTimeMillis() {
return std.datetime.Clock.currStdTime / 10000; return std.datetime.Clock.currStdTime / 10000;
} }
void setStdoutLogger() { synchronized class Log {
logFile = stdout; static {
} private LogLevel logLevel = LogLevel.Info;
private std.stdio.File logFile;
void setStdoutLogger() {
logFile = stdout;
}
void setStderrLogger() { void setStderrLogger() {
logFile = stderr; logFile = stderr;
} }
void setFileLogger(File file) { void setFileLogger(File file) {
logFile = file; logFile = file;
} }
class Log { void setLogLevel(LogLevel level) {
static string logLevelName(LogLevel level) { logLevel = level;
switch (level) { }
case LogLevel.Fatal: return "F";
case LogLevel.Error: return "E"; string logLevelName(LogLevel level) {
case LogLevel.Warn: return "W"; switch (level) {
case LogLevel.Info: return "I"; case LogLevel.Fatal: return "F";
case LogLevel.Debug: return "D"; case LogLevel.Error: return "E";
case LogLevel.Trace: return "V"; case LogLevel.Warn: return "W";
default: return "?"; case LogLevel.Info: return "I";
} case LogLevel.Debug: return "D";
} case LogLevel.Trace: return "V";
static void log(S...)(LogLevel level, S args) { default: return "?";
if (logLevel >= level && logFile.isOpen) { }
SysTime ts = Clock.currTime(); }
logFile.writef("%04d-%02d-%02d %02d:%02d:%02d.%03d %s ", ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.fracSec.msecs, logLevelName(level)); void log(S...)(LogLevel level, S args) {
logFile.writeln(args); if (logLevel >= level && logFile.isOpen) {
logFile.flush(); SysTime ts = Clock.currTime();
} logFile.writef("%04d-%02d-%02d %02d:%02d:%02d.%03d %s ", ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.fracSec.msecs, logLevelName(level));
} logFile.writeln(args);
static void v(S...)(S args) { logFile.flush();
if (logLevel >= LogLevel.Trace && logFile.isOpen) }
log(LogLevel.Trace, args); }
} void v(S...)(S args) {
static void d(S...)(S args) { if (logLevel >= LogLevel.Trace && logFile.isOpen)
if (logLevel >= LogLevel.Debug && logFile.isOpen) log(LogLevel.Trace, args);
log(LogLevel.Debug, args); }
} void d(S...)(S args) {
static void i(S...)(S args) { if (logLevel >= LogLevel.Debug && logFile.isOpen)
if (logLevel >= LogLevel.Info && logFile.isOpen) log(LogLevel.Debug, args);
log(LogLevel.Info, args); }
} void i(S...)(S args) {
static void w(S...)(S args) { if (logLevel >= LogLevel.Info && logFile.isOpen)
if (logLevel >= LogLevel.Warn && logFile.isOpen) log(LogLevel.Info, args);
log(LogLevel.Warn, args); }
} void w(S...)(S args) {
static void e(S...)(S args) { if (logLevel >= LogLevel.Warn && logFile.isOpen)
if (logLevel >= LogLevel.Error && logFile.isOpen) log(LogLevel.Warn, args);
log(LogLevel.Error, args); }
} void e(S...)(S args) {
static void f(S...)(S args) { if (logLevel >= LogLevel.Error && logFile.isOpen)
if (logLevel >= LogLevel.Fatal && logFile.isOpen) log(LogLevel.Error, args);
log(LogLevel.Fatal, args); }
} void f(S...)(S args) {
if (logLevel >= LogLevel.Fatal && logFile.isOpen)
log(LogLevel.Fatal, args);
}
}
} }

View File

@ -32,15 +32,27 @@ enum StandardAction : int {
Save, Save,
} }
__gshared const Action ACTION_OK = new Action(StandardAction.Ok, "ACTION_OK"c); immutable Action ACTION_OK;
__gshared const Action ACTION_CANCEL = new Action(StandardAction.Cancel, "ACTION_CANCEL"c); immutable Action ACTION_CANCEL;
__gshared const Action ACTION_YES = new Action(StandardAction.Yes, "ACTION_YES"c); immutable Action ACTION_YES;
__gshared const Action ACTION_NO = new Action(StandardAction.No, "ACTION_NO"c); immutable Action ACTION_NO;
__gshared const Action ACTION_CLOSE = new Action(StandardAction.Close, "ACTION_CLOSE"c); immutable Action ACTION_CLOSE;
__gshared const Action ACTION_ABORT = new Action(StandardAction.Abort, "ACTION_ABORT"c); immutable Action ACTION_ABORT;
__gshared const Action ACTION_RETRY = new Action(StandardAction.Retry, "ACTION_RETRY"c); immutable Action ACTION_RETRY;
__gshared const Action ACTION_IGNORE = new Action(StandardAction.Ignore, "ACTION_IGNORE"c); immutable Action ACTION_IGNORE;
__gshared const Action ACTION_OPEN = new Action(StandardAction.Open, "ACTION_OPEN"c); immutable Action ACTION_OPEN;
__gshared const Action ACTION_SAVE = new Action(StandardAction.Save, "ACTION_SAVE"c); immutable Action ACTION_SAVE;
static this()
{
ACTION_OK = cast(immutable(Action)) new Action(StandardAction.Ok, "ACTION_OK"c);
ACTION_CANCEL = cast(immutable(Action)) new Action(StandardAction.Cancel, "ACTION_CANCEL"c);
ACTION_YES = cast(immutable(Action)) new Action(StandardAction.Yes, "ACTION_YES"c);
ACTION_NO = cast(immutable(Action)) new Action(StandardAction.No, "ACTION_NO"c);
ACTION_CLOSE = cast(immutable(Action)) new Action(StandardAction.Close, "ACTION_CLOSE"c);
ACTION_ABORT = cast(immutable(Action)) new Action(StandardAction.Abort, "ACTION_ABORT"c);
ACTION_RETRY = cast(immutable(Action)) new Action(StandardAction.Retry, "ACTION_RETRY"c);
ACTION_IGNORE = cast(immutable(Action)) new Action(StandardAction.Ignore, "ACTION_IGNORE"c);
ACTION_OPEN = cast(immutable(Action)) new Action(StandardAction.Open, "ACTION_OPEN"c);
ACTION_SAVE = cast(immutable(Action)) new Action(StandardAction.Save, "ACTION_SAVE"c);
}

View File

@ -39,12 +39,28 @@ private void LVGLFillColor(uint color, float * buf, int count) {
} }
} }
/// for OpenGL calls diagnostics. /* For reporting OpenGL errors, it's nicer to get a human-readable symbolic name for the
private bool checkError(string context, string file = __FILE__, int line = __LINE__) { * error instead of the numeric form. Derelict's GLenum is just an alias for uint, so we
int err = glGetError(); * can't depend on D's nice toString() for enums.
if (err != GL_NO_ERROR) { */
//string errorString = fromStringz(gluErrorString()); private immutable(string[int]) errors;
Log.e("OpenGL error ", err, " at ", file, ":", line, " -- ", context); static this() {
errors = [
0x0500: "GL_INVALID_ENUM",
0x0501: "GL_INVALID_VALUE",
0x0502: "GL_INVALID_OPERATION",
0x0505: "GL_OUT_OF_MEMORY"
];
}
/* Convenient wrapper around glGetError()
* TODO use one of the DEBUG extensions instead
*/
bool checkError(string context="", string file=__FILE__, int line=__LINE__)
{
GLenum err = glGetError();
if (err != GL_NO_ERROR)
{
Log.e("OpenGL error ", err in errors ? errors[err] : to!string(err), " at ", file, ":", line, " -- ", context);
return true; return true;
} }
return false; return false;
@ -526,14 +542,36 @@ class SolidFillProgram : GLProgram {
return false; return false;
beforeExecute(); beforeExecute();
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(
GL_ARRAY_BUFFER,
vertices.length * vertices[0].sizeof + colors.length * colors[0].sizeof,
null,
GL_STREAM_DRAW);
glBufferSubData(
GL_ARRAY_BUFFER,
0,
vertices.length * vertices[0].sizeof,
vertices.ptr);
glBufferSubData(
GL_ARRAY_BUFFER,
vertices.length * vertices[0].sizeof,
colors.length * colors[0].sizeof, colors.ptr);
glEnableVertexAttribArray(vertexLocation); glEnableVertexAttribArray(vertexLocation);
checkError("glEnableVertexAttribArray"); checkError("glEnableVertexAttribArray");
glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, float.sizeof * 3, vertices.ptr); glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0);
checkError("glVertexAttribPointer"); checkError("glVertexAttribPointer");
glEnableVertexAttribArray(colAttrLocation); glEnableVertexAttribArray(colAttrLocation);
checkError("glEnableVertexAttribArray"); checkError("glEnableVertexAttribArray");
glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, float.sizeof * 4, colors.ptr); glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (float.sizeof*3*6));
checkError("glVertexAttribPointer"); checkError("glVertexAttribPointer");
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
@ -545,6 +583,12 @@ class SolidFillProgram : GLProgram {
checkError("glDisableVertexAttribArray"); checkError("glDisableVertexAttribArray");
afterExecute(); afterExecute();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(1, &vbo);
glBindVertexArray(0);
glDeleteVertexArrays(1, &vao);
return true; return true;
} }
} }
@ -600,13 +644,43 @@ class TextureProgram : SolidFillProgram {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);
checkError("drawColorAndTextureRect - glTexParameteri"); checkError("drawColorAndTextureRect - glTexParameteri");
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(
GL_ARRAY_BUFFER,
vertices.length * vertices[0].sizeof +
colors.length * colors[0].sizeof +
texcoords.length * texcoords[0].sizeof,
null,
GL_STREAM_DRAW);
glBufferSubData(
GL_ARRAY_BUFFER,
0,
vertices.length * vertices[0].sizeof,
vertices.ptr);
glBufferSubData(
GL_ARRAY_BUFFER,
vertices.length * vertices[0].sizeof,
colors.length * colors[0].sizeof,
colors.ptr);
glBufferSubData(
GL_ARRAY_BUFFER,
vertices.length * vertices[0].sizeof + colors.length * colors[0].sizeof,
texcoords.length * texcoords[0].sizeof,
texcoords.ptr);
glEnableVertexAttribArray(vertexLocation); glEnableVertexAttribArray(vertexLocation);
glEnableVertexAttribArray(colAttrLocation); glEnableVertexAttribArray(colAttrLocation);
glEnableVertexAttribArray(texCoordLocation); glEnableVertexAttribArray(texCoordLocation);
glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices.ptr); glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, cast(void*) 0);
glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, colors.ptr); glVertexAttribPointer(colAttrLocation, 4, GL_FLOAT, GL_FALSE, 0, cast(void*) (vertices.length * vertices[0].sizeof));
glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, texcoords.ptr); glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, cast(void*) (vertices.length * vertices[0].sizeof + colors.length * colors[0].sizeof));
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
checkError("glDrawArrays"); checkError("glDrawArrays");
@ -616,6 +690,13 @@ class TextureProgram : SolidFillProgram {
glDisableVertexAttribArray(texCoordLocation); glDisableVertexAttribArray(texCoordLocation);
afterExecute(); afterExecute();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(1, &vbo);
glBindVertexArray(0);
glDeleteVertexArrays(1, &vao);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
checkError("glBindTexture"); checkError("glBindTexture");
return true; return true;

View File

@ -26,6 +26,7 @@ import dlangui.core.logger;
import dlangui.core.types; import dlangui.core.types;
import dlangui.graphics.drawbuf; import dlangui.graphics.drawbuf;
import std.stream; import std.stream;
import std.conv : to;
/// load and decode image from file to ColorDrawBuf, returns null if loading or decoding is failed /// load and decode image from file to ColorDrawBuf, returns null if loading or decoding is failed
ColorDrawBuf loadImage(string filename) { ColorDrawBuf loadImage(string filename) {
@ -36,6 +37,7 @@ ColorDrawBuf loadImage(string filename) {
return loadImage(f); return loadImage(f);
} catch (Exception e) { } catch (Exception e) {
Log.e("exception while loading image from file ", filename); Log.e("exception while loading image from file ", filename);
Log.e(to!string(e));
return null; return null;
} }
} }

View File

@ -984,7 +984,7 @@ version(USE_SDL) {
int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow) int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow)
{ {
setFileLogger(std.stdio.File("ui.log", "w")); setFileLogger(std.stdio.File("ui.log", "w"));
setLogLevel(LogLevel.Trace); Log.setLogLevel(LogLevel.Trace);
Log.d("myWinMain()"); Log.d("myWinMain()");
string basePath = exePath(); string basePath = exePath();
Log.i("Current executable: ", exePath()); Log.i("Current executable: ", exePath());
@ -1006,8 +1006,8 @@ version(USE_SDL) {
int main(string[] args) int main(string[] args)
{ {
setStderrLogger(); Log.setStderrLogger();
setLogLevel(LogLevel.Trace); Log.setLogLevel(LogLevel.Warn);
FreeTypeFontManager ft = new FreeTypeFontManager(); FreeTypeFontManager ft = new FreeTypeFontManager();
@ -1054,6 +1054,8 @@ version(USE_SDL) {
// Set OpenGL attributes // Set OpenGL attributes
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
// Share textures between contexts
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
} }
SDLPlatform sdl = new SDLPlatform(); SDLPlatform sdl = new SDLPlatform();

View File

@ -721,9 +721,9 @@ int myWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int
{ {
setFileLogger(std.stdio.File("ui.log", "w")); setFileLogger(std.stdio.File("ui.log", "w"));
debug { debug {
setLogLevel(LogLevel.Trace); Log.setLogLevel(LogLevel.Trace);
} else { } else {
setLogLevel(LogLevel.Info); Log.setLogLevel(LogLevel.Info);
} }
Log.d("myWinMain()"); Log.d("myWinMain()");
string basePath = exePath(); string basePath = exePath();

View File

@ -1131,7 +1131,7 @@ version(USE_XCB) {
{ {
setStderrLogger(); setStderrLogger();
setLogLevel(LogLevel.Trace); Log.setLogLevel(LogLevel.Trace);
FreeTypeFontManager ft = new FreeTypeFontManager(); FreeTypeFontManager ft = new FreeTypeFontManager();
ft.registerFont("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FontFamily.SansSerif, "DejaVu", false, FontWeight.Normal); ft.registerFont("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FontFamily.SansSerif, "DejaVu", false, FontWeight.Normal);

View File

@ -946,14 +946,14 @@ class GridWidgetBase : WidgetGroup, OnScrollHandler {
bool isHeader = x < _headerCols || y < _headerRows; bool isHeader = x < _headerCols || y < _headerRows;
if (phase == 0) { if (phase == 0) {
if (isHeader) if (isHeader)
drawHeaderCellBackground(buf, cellRect, x - _headerCols, y - _headerRows); drawHeaderCellBackground(buf, buf.clipRect, x - _headerCols, y - _headerRows);
else else
drawCellBackground(buf, cellRect, x - _headerCols, y - _headerRows); drawCellBackground(buf, buf.clipRect, x - _headerCols, y - _headerRows);
} else { } else {
if (isHeader) if (isHeader)
drawHeaderCell(buf, cellRect, x - _headerCols, y - _headerRows); drawHeaderCell(buf, buf.clipRect, x - _headerCols, y - _headerRows);
else else
drawCell(buf, cellRect, x - _headerCols, y - _headerRows); drawCell(buf, buf.clipRect, x - _headerCols, y - _headerRows);
} }
} }
} }

View File

@ -527,13 +527,12 @@ class MenuWidgetBase : ListWidget {
// copy item action listeners // copy item action listeners
Signal!MenuItemActionHandler onMenuItemActionListenerCopy = onMenuItemActionListener; Signal!MenuItemActionHandler onMenuItemActionListenerCopy = onMenuItemActionListener;
handleMenuItemClick(item);
PopupWidget popup = cast(PopupWidget)parent; PopupWidget popup = cast(PopupWidget)parent;
if (popup) if (popup)
popup.close(); popup.close();
handleMenuItemClick(item);
// this pointer now can be invalid - if popup removed // this pointer now can be invalid - if popup removed
if (onMenuItemClickListenerCopy.assigned) if (onMenuItemClickListenerCopy.assigned)
if (onMenuItemClickListenerCopy(item)) if (onMenuItemClickListenerCopy(item))