mroe dpi stuff

This commit is contained in:
Adam D. Ruppe 2022-03-22 18:02:03 -04:00
parent 28f09ae118
commit 49829f3f48
3 changed files with 168 additions and 33 deletions

View File

@ -604,7 +604,19 @@ class Widget : ReflectableProperties {
// avoid this it just forwards to a soon-to-be-deprecated function and is not remotely stable
// I'll think up something better eventually
protected final int defaultLineHeight() {
return scaleWithDpi(Window.lineHeight);
auto cs = getComputedStyle();
if(cs.font && !cs.font.isNull)
return cs.font.height() * 5 / 4;
else
return scaleWithDpi(Window.lineHeight);
}
protected final int defaultTextWidth(const(char)[] text) {
auto cs = getComputedStyle();
if(cs.font && !cs.font.isNull)
return cs.font.stringWidth(text);
else
return scaleWithDpi(Window.lineHeight * cast(int) text.length / 2);
}
/++
@ -3951,8 +3963,16 @@ struct StyleInformation {
this.visualTheme = WidgetPainter.visualTheme;
}
/// Forwards to [Widget.Style]
// through the [VisualTheme]
/++
Forwards to [Widget.Style]
Bugs:
It is supposed to fall back to the [VisualTheme] if
the style doesn't override the default, but that is
not generally implemented. Many of them may end up
being explicit overloads instead of the generic
opDispatch fallback, like [font] is now.
+/
public @property opDispatch(string name)() {
typeof(__traits(getMember, Widget.Style.init, name)()) prop;
w.useStyleProperties((scope Widget.Style props) {
@ -3962,6 +3982,25 @@ struct StyleInformation {
return prop;
}
/++
Returns the cached font object associated with the widget,
if overridden by the [Widget.Style|Style], or the [VisualTheme] if not.
History:
Prior to March 21, 2022 (dub v10.7), `font` went through
[opDispatch], which did not use the cache. You can now call it
repeatedly without guilt.
+/
public @property OperatingSystemFont font() {
OperatingSystemFont prop;
w.useStyleProperties((scope Widget.Style props) {
prop = props.fontCached;
});
if(prop is null)
prop = visualTheme.defaultFontCached;
return prop;
}
@property {
// Layout helpers. Currently just forwarding since I haven't made up my mind on a better way.
/** */ int paddingLeft() { return w.paddingLeft(); }
@ -8982,6 +9021,8 @@ class Labeled(T) : Widget {
override int marginTop() { return 4; }
override int marginBottom() { return 4; }
// FIXME: i should prolly call it value as well as content tbh
///
@property string content() {
return lineEdit.content;
@ -10265,10 +10306,10 @@ class MenuItem : MouseActivatedWidget {
override int maxHeight() { return defaultLineHeight + 4; }
override int minHeight() { return defaultLineHeight + 4; }
override int minWidth() { return defaultLineHeight * cast(int) label.length + 8; }
override int minWidth() { return defaultTextWidth(label) + 8 + scaleWithDpi(12); }
override int maxWidth() {
if(cast(MenuBar) parent) {
return defaultLineHeight / 2 * cast(int) label.length + 8;
return minWidth();
}
return int.max;
}
@ -11417,6 +11458,8 @@ abstract class EditableTextWidget : EditableTextWidgetParent {
});
}
private string lastContentBlur;
override void defaultEventHandler_blur(Event ev) {
super.defaultEventHandler_blur(ev);
if(parentWindow.win.closed) return;
@ -11430,8 +11473,11 @@ abstract class EditableTextWidget : EditableTextWidgetParent {
}
}
auto evt = new ChangeEvent!string(this, &this.content);
evt.dispatch();
if(this.content != lastContentBlur) {
auto evt = new ChangeEvent!string(this, &this.content);
evt.dispatch();
lastContentBlur = this.content;
}
}
version(custom_widgets)

View File

@ -559,6 +559,10 @@ interface->SetProgressValue(hwnd, 40, 100);
$(H2 Platform-specific tips and tricks)
X_tips:
On X11, if you set an environment variable, `ARSD_SCALING_FACTOR`, you can control the per-monitor DPI scaling returned to the application. The format is `ARSD_SCALING_FACTOR=2;1`, for example, to set 2x scaling on your first monitor and 1x scaling on your second monitor. Support for this was added on March 22, 2022, the dub 10.7 release.
Windows_tips:
You can add icons or manifest files to your exe using a resource file.
@ -1506,7 +1510,7 @@ string sdpyWindowClass () {
}
/++
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.
Returns the logical 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. This isn't necessarily related to the physical side of the screen; it is associated with a user-defined scaling factor.
If you want per-monitor dpi values, check [SimpleWindow.actualDpi], but you can fall back to this if it returns 0.
+/
@ -1521,37 +1525,52 @@ float[2] getDpi() {
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;
+/
// the physical size isn't actually as important as the logical size since this is
// all about scaling really
dpi[0] = 96;
dpi[1] = 96;
}
char* resourceString = XResourceManagerString(display);
XrmInitialize();
if (resourceString) {
auto db = XrmGetStringDatabase(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 {
auto xft = getXftDpi();
if(xft is float.init)
fallback();
else {
dpi[0] = xft;
dpi[1] = xft;
}
}
return dpi;
}
version(X11)
float getXftDpi() {
auto display = XDisplayConnection.get;
char* resourceString = XResourceManagerString(display);
XrmInitialize();
if (resourceString) {
auto db = XrmGetStringDatabase(resourceString);
XrmValue value;
char* type;
if (XrmGetResource(db, "Xft.dpi", "String", &type, &value) == true) {
if (value.addr) {
import core.stdc.stdlib;
return atof(cast(char*) value.addr);
}
}
}
return float.init;
}
/++
Implementation used by [SimpleWindow.takeScreenshot].
@ -1804,7 +1823,14 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
MonitorInfo.info = MonitorInfo.info[0 .. 0];
MonitorInfo.info.assumeSafeAppend();
foreach(monitor; monitors[0 .. count]) {
foreach(idx, monitor; monitors[0 .. count]) {
MonitorInfo.info ~= MonitorInfo(
Rectangle(Point(monitor.x, monitor.y), Size(monitor.width, monitor.height)),
Size(monitor.mwidth, monitor.mheight),
cast(int) (customScalingFactorForMonitor(cast(int) idx) * 96)
);
/+
if(monitor.mwidth == 0 || monitor.mheight == 0)
// unknown physical size, just guess 96 to avoid divide by zero
MonitorInfo.info ~= MonitorInfo(
@ -1823,8 +1849,9 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
cast(int)(monitor.height * 25.4 / monitor.mheight + 0.5)
)
);
+/
}
//import std.stdio; writeln("Here", MonitorInfo.info);
// import std.stdio; writeln("Here", MonitorInfo.info);
}
}
@ -1845,8 +1872,8 @@ class SimpleWindow : CapableOfHandlingNativeEvent, CapableOfBeingDrawnUpon {
fallback:
// make sure we disable events that aren't coming
xrrEventBase = -1;
// best guess...
actualDpi_ = cast(int) getDpi()[0];
// best guess... respect the custom scaling user command to some extent at least though
actualDpi_ = cast(int) (getDpi()[0] * customScalingFactorForMonitor(0));
}
}
actualDpiLoadAttempted = true;
@ -9902,6 +9929,7 @@ void sdpyPrintDebugString(string fileOverride = null, T...)(T t) nothrow @truste
str ~= "\n";
fwrite(str.ptr, 1, str.length, fp);
fflush(fp);
} catch(Exception e) {
// sorry no hope
}
@ -21491,6 +21519,50 @@ private struct CleanupQueue {
}
private __gshared CleanupQueue cleanupQueue;
version(X11)
/++
Returns the custom scaling factor read out of environment["ARSD_SCALING_FACTOR"].
$(WARNING
This function is exempted from stability guarantees.
)
+/
float customScalingFactorForMonitor(int monitorNumber) {
import core.stdc.stdlib;
auto val = getenv("ARSD_SCALING_FACTOR");
if(val is null)
return 1.0;
char[16] buffer = 0;
int pos;
const(char)* at = val;
foreach(item; 0 .. monitorNumber + 1) {
if(*at == 0)
break; // reuse the last number when we at the end of the string
pos = 0;
while(pos + 1 < buffer.length && *at && *at != ';') {
buffer[pos++] = *at;
at++;
}
if(*at)
at++; // skip the semicolon
buffer[pos] = 0;
}
//sdpyPrintDebugString(buffer[0 .. pos]);
import core.stdc.math;
auto f = atof(buffer.ptr);
if(f <= 0.0 || isnan(f) || isinf(f))
return 1.0;
return f;
}
void guiAbortProcess(string msg) {
import core.stdc.stdlib;
version(Windows) {

View File

@ -8245,7 +8245,9 @@ version(TerminalDirectToEmulator) {
On January 12, 2022, I changed the font size to be auto-scaled
with detected dpi by default. You can undo this by setting
`scaleFontSizeWithDpi` to false.
`scaleFontSizeWithDpi` to false. On March 22, 2022, I tweaked
this slightly to only scale if the font point size is not already
scaled (e.g. by Xft.dpi settings) to avoid double scaling.
+/
string fontName = defaultFont;
/// ditto
@ -8954,7 +8956,22 @@ version(TerminalDirectToEmulator) {
}
auto fontSize = integratedTerminalEmulatorConfiguration.fontSize;
if(integratedTerminalEmulatorConfiguration.scaleFontSizeWithDpi) {
fontSize = widget.scaleWithDpi(fontSize);
static if(UsingSimpledisplayX11) {
// if it is an xft font and xft is already scaled, we should NOT double scale.
import std.algorithm;
if(integratedTerminalEmulatorConfiguration.fontName.startsWith("core:")) {
// core font doesn't use xft anyway
fontSize = widget.scaleWithDpi(fontSize);
} else {
auto xft = getXftDpi();
if(xft is float.init)
xft = 96;
fontSize = widget.scaleWithDpi(fontSize, cast(int) xft);
}
} else {
fontSize = widget.scaleWithDpi(fontSize);
}
}
if(integratedTerminalEmulatorConfiguration.fontName.length) {