FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Implement a platform abstraction for windows. · ppd/solvespace@f324477 · GitHub

Implement a platform abstraction for windows. · ppd/solvespace@f324477 · GitHub
Skip to content

Navigation Menu

Repository navigation

Commit f324477

Browse files
committed
Implement a platform abstraction for windows.
This commit removes a large amount of code partially duplicated between the text and the graphics windows, and opens the path to having more than one model window on screen at any given time, as well as simplifies platform work. This commit also adds complete support for High-DPI device pixel ratio. It adds support for font scale factor (a fractional factor on top of integral device pixel ratio) on the platform side, but not on the application side. This commit also adds error checking to all Windows API calls (within the abstracted code) and fixes a significant number of misuses and non-future-proof uses of Windows API. This commit also makes uses of Windows API idiomatic, e.g. using the built-in vertical scroll bar, native tooltips, control subclassing instead of hooks in the global dispatch loop, and so on. It reinstates tooltip support and removes menu-related hacks.
1 parent 55baaf3 commit f324477

59 files changed

Lines changed: 3066 additions & 2703 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ New measurement/analysis features:
5959

6060
Other new features:
6161
* New command-line interface, for batch exporting and more.
62+
* The graphical interface now supports HiDPI screens on every OS.
6263
* New link to match the on-screen size of the sketch with its actual size,
6364
"view → set to full scale".
6465
* When zooming to fit, constraints are also considered.

‎res/CMakeLists.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ endfunction()
118118
# Second, register all resources.
119119
if(WIN32)
120120
add_resource(win32/icon.ico 4000 ICON)
121-
add_resource(win32/manifest.xml 2 RT_MANIFEST)
121+
add_resource(win32/manifest.xml 1 RT_MANIFEST)
122122
elseif(APPLE)
123123
add_iconset (cocoa/AppIcon.iconset)
124124
add_xib (cocoa/MainMenu.xib)

‎res/win32/manifest.xml‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
2+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0">
33
<assemblyIdentity
44
version="1.0.0.0"
55
processorArchitecture="*"
66
name="JonathanWesthues.3dCAD.SolveSpace"
77
type="win32"
88
/>
9-
<application xmlns="urn:schemas-microsoft-com:asm.v3">
10-
<windowsSettings>
11-
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
12-
</windowsSettings>
13-
</application>
149
<description>Parametric 3d CAD tool.</description>
10+
<asmv3:application>
11+
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
12+
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
13+
<dpiAware>true</dpiAware>
14+
</asmv3:windowsSettings>
15+
</asmv3:application>
1516
<dependency>
1617
<dependentAssembly>
1718
<assemblyIdentity

‎src/CMakeLists.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ if(ENABLE_GUI)
329329

330330
if(MSVC)
331331
set_target_properties(solvespace PROPERTIES
332-
LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF")
332+
LINK_FLAGS "/MANIFEST:NO /SAFESEH:NO /INCREMENTAL:NO /OPT:REF /STACK:33554432")
333333
endif()
334334
endif()
335335

‎src/clipboard.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ void GraphicsWindow::MenuClipboard(Command id) {
325325
}
326326
}
327327

328-
bool TextWindow::EditControlDoneForPaste(const char *s) {
328+
bool TextWindow::EditControlDoneForPaste(const std::string &s) {
329329
Expr *e;
330330
switch(edit.meaning) {
331331
case Edit::PASTE_TIMES_REPEATED: {

‎src/confscreen.cpp‎

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -80,28 +80,27 @@ void TextWindow::ScreenChangeFixExportColors(int link, uint32_t v) {
8080

8181
void TextWindow::ScreenChangeBackFaces(int link, uint32_t v) {
8282
SS.drawBackFaces = !SS.drawBackFaces;
83-
SS.GW.persistentDirty = true;
84-
InvalidateGraphics();
83+
SS.GW.Invalidate(/*clearPersistent=*/true);
8584
}
8685

8786
void TextWindow::ScreenChangeShowContourAreas(int link, uint32_t v) {
8887
SS.showContourAreas = !SS.showContourAreas;
89-
InvalidateGraphics();
88+
SS.GW.Invalidate();
9089
}
9190

9291
void TextWindow::ScreenChangeCheckClosedContour(int link, uint32_t v) {
9392
SS.checkClosedContour = !SS.checkClosedContour;
94-
InvalidateGraphics();
93+
SS.GW.Invalidate();
9594
}
9695

9796
void TextWindow::ScreenChangeShadedTriangles(int link, uint32_t v) {
9897
SS.exportShadedTriangles = !SS.exportShadedTriangles;
99-
InvalidateGraphics();
98+
SS.GW.Invalidate();
10099
}
101100

102101
void TextWindow::ScreenChangePwlCurves(int link, uint32_t v) {
103102
SS.exportPwlCurves = !SS.exportPwlCurves;
104-
InvalidateGraphics();
103+
SS.GW.Invalidate();
105104
}
106105

107106
void TextWindow::ScreenChangeCanvasSizeAuto(int link, uint32_t v) {
@@ -110,7 +109,7 @@ void TextWindow::ScreenChangeCanvasSizeAuto(int link, uint32_t v) {
110109
} else {
111110
SS.exportCanvasSizeAuto = false;
112111
}
113-
InvalidateGraphics();
112+
SS.GW.Invalidate();
114113
}
115114

116115
void TextWindow::ScreenChangeCanvasSize(int link, uint32_t v) {
@@ -321,26 +320,26 @@ void TextWindow::ShowConfiguration() {
321320
}
322321
}
323322

324-
bool TextWindow::EditControlDoneForConfiguration(const char *s) {
323+
bool TextWindow::EditControlDoneForConfiguration(const std::string &s) {
325324
switch(edit.meaning) {
326325
case Edit::LIGHT_INTENSITY:
327-
SS.lightIntensity[edit.i] = min(1.0, max(0.0, atof(s)));
328-
InvalidateGraphics();
326+
SS.lightIntensity[edit.i] = min(1.0, max(0.0, atof(s.c_str())));
327+
SS.GW.Invalidate();
329328
break;
330329

331330
case Edit::LIGHT_DIRECTION: {
332331
double x, y, z;
333-
if(sscanf(s, "%lf, %lf, %lf", &x, &y, &z)==3) {
332+
if(sscanf(s.c_str(), "%lf, %lf, %lf", &x, &y, &z)==3) {
334333
SS.lightDir[edit.i] = Vector::From(x, y, z);
335334
} else {
336335
Error(_("Bad format: specify coordinates as x, y, z"));
337336
}
338-
InvalidateGraphics();
337+
SS.GW.Invalidate();
339338
break;
340339
}
341340
case Edit::COLOR: {
342341
Vector rgb;
343-
if(sscanf(s, "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
342+
if(sscanf(s.c_str(), "%lf, %lf, %lf", &rgb.x, &rgb.y, &rgb.z)==3) {
344343
rgb = rgb.ClampWithin(0, 1);
345344
SS.modelColor[edit.i] = RGBf(rgb.x, rgb.y, rgb.z);
346345
} else {
@@ -350,44 +349,44 @@ bool TextWindow::EditControlDoneForConfiguration(const char *s) {
350349
}
351350
case Edit::CHORD_TOLERANCE: {
352351
if(edit.i == 0) {
353-
SS.chordTol = max(0.0, atof(s));
352+
SS.chordTol = max(0.0, atof(s.c_str()));
354353
SS.GenerateAll(SolveSpaceUI::Generate::ALL);
355354
} else {
356-
SS.exportChordTol = max(0.0, atof(s));
355+
SS.exportChordTol = max(0.0, atof(s.c_str()));
357356
}
358357
break;
359358
}
360359
case Edit::MAX_SEGMENTS: {
361360
if(edit.i == 0) {
362-
SS.maxSegments = min(1000, max(7, atoi(s)));
361+
SS.maxSegments = min(1000, max(7, atoi(s.c_str())));
363362
SS.GenerateAll(SolveSpaceUI::Generate::ALL);
364363
} else {
365-
SS.exportMaxSegments = min(1000, max(7, atoi(s)));
364+
SS.exportMaxSegments = min(1000, max(7, atoi(s.c_str())));
366365
}
367366
break;
368367
}
369368
case Edit::CAMERA_TANGENT: {
370-
SS.cameraTangent = (min(2.0, max(0.0, atof(s))))/1000.0;
369+
SS.cameraTangent = (min(2.0, max(0.0, atof(s.c_str()))))/1000.0;
371370
if(!SS.usePerspectiveProj) {
372371
Message(_("The perspective factor will have no effect until you "
373372
"enable View -> Use Perspective Projection."));
374373
}
375-
InvalidateGraphics();
374+
SS.GW.Invalidate();
376375
break;
377376
}
378377
case Edit::GRID_SPACING: {
379378
SS.gridSpacing = (float)min(1e4, max(1e-3, SS.StringToMm(s)));
380-
InvalidateGraphics();
379+
SS.GW.Invalidate();
381380
break;
382381
}
383382
case Edit::DIGITS_AFTER_DECIMAL: {
384-
int v = atoi(s);
383+
int v = atoi(s.c_str());
385384
if(v < 0 || v > 8) {
386385
Error(_("Specify between 0 and 8 digits after the decimal."));
387386
} else {
388387
SS.SetUnitDigitsAfterDecimal(v);
389388
}
390-
InvalidateGraphics();
389+
SS.GW.Invalidate();
391390
break;
392391
}
393392
case Edit::EXPORT_SCALE: {
@@ -455,8 +454,8 @@ bool TextWindow::EditControlDoneForConfiguration(const char *s) {
455454
break;
456455
}
457456
case Edit::AUTOSAVE_INTERVAL: {
458-
int interval;
459-
if(sscanf(s, "%d", &interval)==1) {
457+
int interval = atoi(s.c_str());
458+
if(interval) {
460459
if(interval >= 1) {
461460
SS.autosaveInterval = interval;
462461
SS.ScheduleAutosave();

‎src/constraint.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ void Constraint::MenuConstrain(Command id) {
744744
}
745745

746746
SS.GW.ClearSelection();
747-
InvalidateGraphics();
747+
SS.GW.Invalidate();
748748
}
749749

750750
#endif /* ! LIBRARY */

‎src/draw.cpp‎

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ void GraphicsWindow::Selection::Draw(bool isHovered, Canvas *canvas) {
8080
void GraphicsWindow::ClearSelection() {
8181
selection.Clear();
8282
SS.ScheduleShowTW();
83-
InvalidateGraphics();
83+
Invalidate();
8484
}
8585

8686
void GraphicsWindow::ClearNonexistentSelectionItems() {
@@ -98,7 +98,7 @@ void GraphicsWindow::ClearNonexistentSelectionItems() {
9898
}
9999
}
100100
selection.RemoveTagged();
101-
if(change) InvalidateGraphics();
101+
if(change) Invalidate();
102102
}
103103

104104
//-----------------------------------------------------------------------------
@@ -304,14 +304,14 @@ void GraphicsWindow::GroupSelection() {
304304

305305
Camera GraphicsWindow::GetCamera() const {
306306
Camera camera = {};
307-
camera.width = (int)width;
308-
camera.height = (int)height;
309-
camera.offset = offset;
310-
camera.projUp = projUp;
311-
camera.projRight = projRight;
312-
camera.scale = scale;
313-
camera.tangent = SS.CameraTangent();
314-
camera.hasPixels = true;
307+
window->GetContentSize(&camera.width, &camera.height);
308+
camera.pixelRatio = window->GetDevicePixelRatio();
309+
camera.gridFit = (window->GetDevicePixelRatio() == 1);
310+
camera.offset = offset;
311+
camera.projUp = projUp;
312+
camera.projRight = projRight;
313+
camera.scale = scale;
314+
camera.tangent = SS.CameraTangent();
315315
return camera;
316316
}
317317

@@ -457,7 +457,7 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
457457

458458
if(!sel.Equals(&hover)) {
459459
hover = sel;
460-
InvalidateGraphics();
460+
Invalidate();
461461
}
462462
}
463463

@@ -545,6 +545,10 @@ void GraphicsWindow::NormalizeProjectionVectors() {
545545
void GraphicsWindow::DrawSnapGrid(Canvas *canvas) {
546546
if(!LockedInWorkplane()) return;
547547

548+
const Camera &camera = canvas->GetCamera();
549+
double width = camera.width,
550+
height = camera.height;
551+
548552
hEntity he = ActiveWorkplane();
549553
EntityBase *wrkpl = SK.GetEntity(he),
550554
*norm = wrkpl->Normal();
@@ -816,15 +820,11 @@ void GraphicsWindow::Draw(Canvas *canvas) {
816820
}
817821

818822
void GraphicsWindow::Paint() {
819-
if(!canvas) return;
823+
ssassert(window != NULL && canvas != NULL,
824+
"Cannot paint without window and canvas");
820825

821826
havePainted = true;
822827

823-
int w, h;
824-
GetGraphicsWindowSize(&w, &h);
825-
width = w;
826-
height = h;
827-
828828
Camera camera = GetCamera();
829829
Lighting lighting = GetLighting();
830830

@@ -902,3 +902,12 @@ void GraphicsWindow::Paint() {
902902
canvas->FlushFrame();
903903
canvas->Clear();
904904
}
905+
906+
void GraphicsWindow::Invalidate(bool clearPersistent) {
907+
if(window) {
908+
if(clearPersistent) {
909+
persistentDirty = true;
910+
}
911+
window->Invalidate();
912+
}
913+
}

‎src/export.cpp‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ void SolveSpaceUI::ExportViewOrWireframeTo(const Platform::Path &filename, bool
263263
}
264264

265265
SS.justExportedInfo.draw = true;
266-
InvalidateGraphics();
266+
GW.Invalidate();
267267
}
268268

269269
edges.Clear();
@@ -839,7 +839,7 @@ void SolveSpaceUI::ExportMeshTo(const Platform::Path &filename) {
839839

840840
SS.justExportedInfo.showOrigin = false;
841841
SS.justExportedInfo.draw = true;
842-
InvalidateGraphics();
842+
GW.Invalidate();
843843
}
844844

845845
//-----------------------------------------------------------------------------
@@ -1108,5 +1108,5 @@ void SolveSpaceUI::ExportMeshAsThreeJsTo(FILE *f, const Platform::Path &filename
11081108
void SolveSpaceUI::ExportAsPngTo(const Platform::Path &filename) {
11091109
screenshotFile = filename;
11101110
// The rest of the work is done in the next redraw.
1111-
InvalidateGraphics();
1111+
GW.Invalidate();
11121112
}

‎src/expr.cpp‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,7 @@ class ExprParser {
605605
bool IsError() const { return type == TokenType::ERROR; }
606606
};
607607

608-
const char *input;
609-
unsigned inputPos;
608+
std::string::const_iterator it, end;
610609
std::vector<Token> stack;
611610

612611
char ReadChar();
@@ -624,7 +623,7 @@ class ExprParser {
624623
bool Reduce(std::string *error);
625624
bool Parse(std::string *error, size_t reduceUntil = 0);
626625

627-
static Expr *Parse(const char *input, std::string *error);
626+
static Expr *Parse(const std::string &input, std::string *error);
628627
};
629628

630629
ExprParser::Token ExprParser::Token::From(TokenType type, Expr *expr) {
@@ -643,11 +642,15 @@ ExprParser::Token ExprParser::Token::From(TokenType type, Expr::Op op) {
643642
}
644643

645644
char ExprParser::ReadChar() {
646-
return input[inputPos++];
645+
return *it++;
647646
}
648647

649648
char ExprParser::PeekChar() {
650-
return input[inputPos];
649+
if(it == end) {
650+
return '\0';
651+
} else {
652+
return *it;
653+
}
651654
}
652655

653656
std::string ExprParser::ReadWord() {
@@ -889,28 +892,29 @@ bool ExprParser::Parse(std::string *error, size_t reduceUntil) {
889892
return true;
890893
}
891894

892-
Expr *ExprParser::Parse(const char *input, std::string *error) {
895+
Expr *ExprParser::Parse(const std::string &input, std::string *error) {
893896
ExprParser parser;
894-
parser.input = input;
895-
parser.inputPos = 0;
897+
parser.it = input.cbegin();
898+
parser.end = input.cend();
896899
if(!parser.Parse(error)) return NULL;
897900

898901
Token r = parser.PopOperand(error);
899902
if(r.IsError()) return NULL;
900903
return r.expr;
901904
}
902905

903-
Expr *Expr::Parse(const char *input, std::string *error) {
906+
Expr *Expr::Parse(const std::string &input, std::string *error) {
904907
return ExprParser::Parse(input, error);
905908
}
906909

907-
Expr *Expr::From(const char *input, bool popUpError) {
910+
Expr *Expr::From(const std::string &input, bool popUpError) {
908911
std::string error;
909912
Expr *e = ExprParser::Parse(input, &error);
910913
if(!e) {
911914
dbp("Parse/lex error: %s", error.c_str());
912915
if(popUpError) {
913-
Error("Not a valid number or expression: '%s'.\n%s.", input, error.c_str());
916+
Error("Not a valid number or expression: '%s'.\n%s.",
917+
input.c_str(), error.c_str());
914918
}
915919
}
916920
return e;

0 commit comments

Comments
 (0)

Footer

© 2026 GitHub, Inc.

Back | FazBrowse Home | New Git URL