more grid size fixes and thread helpers

This commit is contained in:
Adam D. Ruppe 2021-12-03 21:42:07 -05:00
parent 333e1ebef8
commit 4248b8e8a2
3 changed files with 187 additions and 18 deletions

View File

@ -46,7 +46,7 @@ private {
}
package(arsd) @trusted
string toInternal(T)(int a) {
string toInternal(T)(long a) {
if(a == 0)
return "0";
char[] ret;

View File

@ -7733,14 +7733,45 @@ class TableView : Widget {
/// Passed to [setColumnInfo]
static struct ColumnInfo {
const(char)[] name; /// the name displayed in the header
int width; /// the default width, in pixels
TextAlignment alignment; /// alignment of the text in the cell
/++
The default width, in pixels. As a special case, you can set this to -1
if you want the system to try to automatically size the width to fit visible
content. If it can't, it will try to pick a sensible default size.
Any other negative value is not allowed and may lead to unpredictable results.
History:
The -1 behavior was specified on December 3, 2021. It actually worked before
anyway on Win32 but now it is a formal feature with partial Linux support.
Bugs:
It doesn't actually attempt to calculate a best-fit width on Linux as of
December 3, 2021. I do plan to fix this in the future, but Windows is the
priority right now. At least it doesn't break things when you use it now.
+/
int width;
/++
Alignment of the text in the cell. Applies to the header as well as all data in this
column.
Bugs:
On Windows, the first column ignores this member and is always left aligned.
You can work around this by inserting a dummy first column with width = 0
then putting your actual data in the second column, which does respect the
alignment.
This is a quirk of the operating system's implementation going back a very
long time and is unlikely to ever be fixed.
+/
TextAlignment alignment;
/++
After all the pixel widths have been assigned, any left over
space is divided up among all columns and distributed to according
to the widthPercent field.
For example, if you have two fields, both with width 50 and one with
widthPercent of 25 and the other with widthPercent of 75, and the
container is 200 pixels wide, first both get their width of 50.
@ -7761,6 +7792,17 @@ class TableView : Widget {
The percents total in the column can never exceed 100 or be less than 0.
Doing this will trigger an assert error.
Implementation note:
Please note that percentages are only recalculated 1) upon original
construction and 2) upon resizing the control. If the user adjusts the
width of a column, the percentage items will not be updated.
On the other hand, if the user adjusts the width of a percentage column
then resizes the window, it is recalculated, meaning their hand adjustment
is discarded. This specific behavior may change in the future as it is
arguably a bug, but I'm not certain yet.
History:
Added November 10, 2021 (dub v10.4)
+/
@ -7777,8 +7819,9 @@ class TableView : Widget {
+/
void setColumnInfo(ColumnInfo[] columns...) {
foreach(ref c; this.columns)
foreach(ref c; columns) {
c.name = c.name.idup;
}
this.columns = columns.dup;
updateCalculatedWidth(false);
@ -7787,10 +7830,10 @@ class TableView : Widget {
tvwi.header.updateHeaders();
tvwi.updateScrolls();
} else version(win32_widgets)
foreach(i, ref column; columns) {
foreach(i, column; this.columns) {
LVCOLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.cx = column.calculatedWidth;
lvColumn.cx = column.width == -1 ? -1 : column.calculatedWidth;
auto bfr = WCharzBuffer(column.name);
lvColumn.pszText = bfr.ptr;
@ -7807,19 +7850,35 @@ class TableView : Widget {
}
}
private void updateCalculatedWidth(bool informWindows) {
private int getActualSetSize(size_t i, bool askWindows) {
version(win32_widgets)
if(askWindows)
return SendMessage(hwnd, LVM_GETCOLUMNWIDTH, cast(WPARAM) i, 0);
auto w = columns[i].width;
if(w == -1)
return 50; // idk, just give it some space so the percents aren't COMPLETELY off
return w;
}
private void updateCalculatedWidth(bool informWindows) {
int padding;
version(win32_widgets)
padding = 4;
int remaining = this.width;
foreach(column; columns)
remaining -= column.width;
foreach(i, column; columns)
remaining -= this.getActualSetSize(i, informWindows && column.widthPercent == 0) + padding;
remaining -= padding;
if(remaining < 0)
remaining = 0;
int percentTotal;
foreach(i, ref column; columns) {
percentTotal += column.widthPercent;
auto c = column.width + (remaining * column.widthPercent) / 100;
auto c = this.getActualSetSize(i, informWindows && column.widthPercent == 0) + (remaining * column.widthPercent) / 100;
column.calculatedWidth = c;
version(win32_widgets)
if(informWindows)
SendMessage(hwnd, LVM_SETCOLUMNWIDTH, i, c); // LVSCW_AUTOSIZE or LVSCW_AUTOSIZE_USEHEADER are amazing omg
@ -8098,8 +8157,6 @@ class TableView : Widget {
+/
CellStyle delegate(int row, int column) getCellStyle;
// i want to be able to do things like draw little colored things to show red for negative numbers
// or background color indicators or even in-cell charts
// void delegate(int row, int column, WidgetPainter painter, int width, int height, in char[] text) drawCell;
@ -8195,6 +8252,10 @@ private class TableViewWidgetInner : Widget {
break;
x = 0;
foreach(columnNumber, column; tvw.columns) {
if(column.width == 0)
continue;
auto x2 = x + column.calculatedWidth;
auto smwx = smw.position.x;
@ -8280,6 +8341,8 @@ private class TableViewWidgetInner : Widget {
child.removeWidget();
foreach(column; tvw.tvw.columns) {
if(column.width == 0)
continue;
// the cast is ok because I dup it above, just the type is never changed.
// all this is private so it should never get messed up.
new Button(ImageLabel(cast(string) column.name, column.alignment), this);
@ -12462,7 +12525,14 @@ enum GenericIcons : ushort {
Print, ///
}
///
/++
History:
The dialog itself on Linux was modified on December 2, 2021 to include
a directory picker in addition to the command line completion view.
Future_directions:
I want to add some kind of custom preview and maybe thumbnail thing in the future,
at least on Linux, maybe on Windows too.
+/
void getOpenFileName(
void delegate(string) onOK,
string prefilledName = null,

View File

@ -1630,8 +1630,8 @@ shared static this() {
auto lib = LoadLibrary("User32.dll");
if(lib is null)
return;
scope(exit)
FreeLibrary(lib);
//scope(exit)
//FreeLibrary(lib);
SetProcessDpiAwarenessContext_t SetProcessDpiAwarenessContext = cast(SetProcessDpiAwarenessContext_t) GetProcAddress(lib, "SetProcessDpiAwarenessContext");
@ -9565,7 +9565,7 @@ private void runPendingRunInGuiThreadDelegates() {
private void claimGuiThread() nothrow {
import core.atomic;
if(cas(&guiThreadExists, false, true))
if(cas(&guiThreadExists_, false, true))
thisIsGuiThread = true;
}
@ -9579,9 +9579,108 @@ private struct RunQueueMember {
private __gshared RunQueueMember*[] runInGuiThreadQueue;
private __gshared Object runInGuiThreadLock = new Object; // intentional CTFE
private bool thisIsGuiThread = false;
private shared bool guiThreadExists = false;
private shared bool guiThreadExists_ = false;
private shared bool guiThreadTerminating = false;
/++
Returns `true` if a gui thread exists, that is, a thread running the simpledisplay.d
event loop. All windows must be exclusively created and managed by a single thread.
If no gui thread exists, simpledisplay.d will automatically adopt the current thread
when you call one of its constructors.
If a gui thread exists, you should check [thisThreadRunningGui] to see if it is this
one. If so, you can run gui functions on it. If not, don't. The helper functions
[runInGuiThread] and [runInGuiThreadAsync] can be used to help you with this automatically.
The reason this function is available is in case you want to message pass between a gui
thread and your current thread. If no gui thread exists or if this is the gui thread,
you're liable to deadlock when trying to communicate since you'd end up talking to yourself.
History:
Added December 3, 2021 (dub v10.5)
+/
public bool guiThreadExists() {
return guiThreadExists_;
}
/++
Returns `true` if this thread is either running or set to be running the
simpledisplay.d gui core event loop because it owns windows.
It is important to keep gui-related functionality in the right thread, so you will
want to `runInGuiThread` when you call them (with some specific exceptions called
out in those specific functions' documentation). Notably, all windows must be
created and managed only from the gui thread.
Will return false if simpledisplay's other functions haven't been called
yet; check [guiThreadExists] in addition to this.
History:
Added December 3, 2021 (dub v10.5)
+/
public bool thisThreadRunningGui() {
return thisIsGuiThread;
}
/++
Function to help temporarily print debugging info. It will bypass any stdout/err redirection
and go to the controlling tty or console (attaching to the parent and/or allocating one as
needed on Windows. Please note it may overwrite output from other programs in the parent and the
allocated one will not survive if your program crashes. Use the `fileOverride` to print to a log
file instead if you are in one of those situations).
It does not support outputting very many types; just strings and ints are likely to actually work.
It will perform very slowly and swallows any errors that may occur. Moreover, the specific output
is unspecified meaning I can change it at any time. The only point of this function is to help
in temporary use for printf-style debugging. It is NOT nogc, but you can use the `debug` keyword
and the compiler will cheat for you. It is, however, formally nothrow and trusted to ease its use
in those contexts.
$(WARNING
I reserve the right to change this function at any time. You can use it if it helps you
but do not rely on it for anything permanent.
)
History:
Added December 3, 2021. Not formally supported under any stable tag.
+/
void sdpyPrintDebugString(string fileOverride = null, T...)(T t) nothrow @trusted {
try {
version(Windows) {
import core.sys.windows.windows;
if(AttachConsole(ATTACH_PARENT_PROCESS))
AllocConsole();
const(char)* fn = "CONOUT$";
} else version(Posix) {
const(char)* fn = "/dev/tty";
} else static assert(0, "Function not implemented for your system");
if(fileOverride.length)
fn = fileOverride.ptr;
import core.stdc.stdio;
auto fp = fopen(fn, "wt");
if(fp is null) return;
scope(exit) fclose(fp);
string str;
foreach(item; t) {
static if(is(typeof(item) : const(char)[]))
str ~= item;
else
str ~= toInternal!string(item);
str ~= " ";
}
str ~= "\n";
fwrite(str.ptr, 1, str.length, fp);
} catch(Exception e) {
// sorry no hope
}
}
private void guiThreadFinalize() {
assert(thisIsGuiThread);
@ -11345,7 +11444,7 @@ version(Windows) {
// doing this because of https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/DPIAwarenessPerWindow/client/DpiAwarenessContext.cpp
// im not sure it is completely correct
// but without it the tabs and such do look weird as things change.
{
if(SystemParametersInfoForDpi) {
LOGFONT lfText;
SystemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, lfText.sizeof, &lfText, FALSE, this.actualDpi_);
HFONT hFontNew = CreateFontIndirect(&lfText);