Implement a platform abstraction for timers.
This commit temporarily disables tooltip functionality; it will be handled natively in a platform abstraction for windows using much simpler code.
This commit is contained in:
parent
28f94dcd0a
commit
7ab87caa88
@ -105,6 +105,7 @@ endif()
|
|||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(platform_SOURCES
|
set(platform_SOURCES
|
||||||
platform/w32main.cpp
|
platform/w32main.cpp
|
||||||
|
platform/guiwin.cpp
|
||||||
${gl_SOURCES})
|
${gl_SOURCES})
|
||||||
|
|
||||||
set(platform_LIBRARIES
|
set(platform_LIBRARIES
|
||||||
@ -116,12 +117,13 @@ elseif(APPLE)
|
|||||||
|
|
||||||
set(platform_SOURCES
|
set(platform_SOURCES
|
||||||
platform/cocoamain.mm
|
platform/cocoamain.mm
|
||||||
|
platform/guimac.mm
|
||||||
render/rendergl.cpp
|
render/rendergl.cpp
|
||||||
${gl_SOURCES})
|
${gl_SOURCES})
|
||||||
else()
|
else()
|
||||||
set(platform_SOURCES
|
set(platform_SOURCES
|
||||||
platform/gtkmain.cpp
|
platform/gtkmain.cpp
|
||||||
render/rendergl.cpp
|
platform/guigtk.cpp
|
||||||
${gl_SOURCES})
|
${gl_SOURCES})
|
||||||
|
|
||||||
set(platform_LIBRARIES
|
set(platform_LIBRARIES
|
||||||
@ -333,7 +335,7 @@ endif()
|
|||||||
# solvespace headless library
|
# solvespace headless library
|
||||||
|
|
||||||
set(headless_SOURCES
|
set(headless_SOURCES
|
||||||
platform/headless.cpp
|
platform/guinone.cpp
|
||||||
render/rendercairo.cpp)
|
render/rendercairo.cpp)
|
||||||
|
|
||||||
add_library(solvespace-headless STATIC EXCLUDE_FROM_ALL
|
add_library(solvespace-headless STATIC EXCLUDE_FROM_ALL
|
||||||
|
@ -459,7 +459,7 @@ bool TextWindow::EditControlDoneForConfiguration(const char *s) {
|
|||||||
if(sscanf(s, "%d", &interval)==1) {
|
if(sscanf(s, "%d", &interval)==1) {
|
||||||
if(interval >= 1) {
|
if(interval >= 1) {
|
||||||
SS.autosaveInterval = interval;
|
SS.autosaveInterval = interval;
|
||||||
SetAutosaveTimerFor(interval);
|
SS.ScheduleAutosave();
|
||||||
} else {
|
} else {
|
||||||
Error(_("Bad value: autosave interval should be positive"));
|
Error(_("Bad value: autosave interval should be positive"));
|
||||||
}
|
}
|
||||||
|
@ -468,11 +468,13 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::ClearPending() {
|
void GraphicsWindow::ClearPending(bool scheduleShowTW) {
|
||||||
pending.points.Clear();
|
pending.points.Clear();
|
||||||
pending.requests.Clear();
|
pending.requests.Clear();
|
||||||
pending = {};
|
pending = {};
|
||||||
SS.ScheduleShowTW();
|
if(scheduleShowTW) {
|
||||||
|
SS.ScheduleShowTW();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GraphicsWindow::IsFromPending(hRequest r) {
|
bool GraphicsWindow::IsFromPending(hRequest r) {
|
||||||
@ -1536,7 +1538,6 @@ void GraphicsWindow::MouseLeave() {
|
|||||||
// currently a context menu shown.
|
// currently a context menu shown.
|
||||||
if(!context.active) {
|
if(!context.active) {
|
||||||
hover.Clear();
|
hover.Clear();
|
||||||
toolbarTooltipped = Command::NONE;
|
|
||||||
toolbarHovered = Command::NONE;
|
toolbarHovered = Command::NONE;
|
||||||
PaintGraphics();
|
PaintGraphics();
|
||||||
}
|
}
|
||||||
|
@ -62,52 +62,6 @@ std::string CnfThawString(const std::string &val, const std::string &key) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Timer */
|
|
||||||
|
|
||||||
@interface DeferredHandler : NSObject
|
|
||||||
+ (void) runLater:(id)dummy;
|
|
||||||
+ (void) runCallback;
|
|
||||||
+ (void) doAutosave;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation DeferredHandler
|
|
||||||
+ (void) runLater:(id)dummy {
|
|
||||||
SolveSpace::SS.DoLater();
|
|
||||||
}
|
|
||||||
+ (void) runCallback {
|
|
||||||
SolveSpace::SS.GW.TimerCallback();
|
|
||||||
SolveSpace::SS.TW.TimerCallback();
|
|
||||||
}
|
|
||||||
+ (void) doAutosave {
|
|
||||||
SolveSpace::SS.Autosave();
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
|
|
||||||
static void Schedule(SEL selector, double interval) {
|
|
||||||
NSMethodSignature *signature = [[DeferredHandler class]
|
|
||||||
methodSignatureForSelector:selector];
|
|
||||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
|
|
||||||
[invocation setSelector:selector];
|
|
||||||
[invocation setTarget:[DeferredHandler class]];
|
|
||||||
[NSTimer scheduledTimerWithTimeInterval:interval
|
|
||||||
invocation:invocation repeats:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
void SolveSpace::SetTimerFor(int milliseconds) {
|
|
||||||
Schedule(@selector(runCallback), milliseconds / 1000.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SolveSpace::SetAutosaveTimerFor(int minutes) {
|
|
||||||
Schedule(@selector(doAutosave), minutes * 60.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SolveSpace::ScheduleLater() {
|
|
||||||
[[NSRunLoop currentRunLoop]
|
|
||||||
performSelector:@selector(runLater:)
|
|
||||||
target:[DeferredHandler class] argument:nil
|
|
||||||
order:0 modes:@[NSDefaultRunLoopMode]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OpenGL view */
|
/* OpenGL view */
|
||||||
|
|
||||||
@interface GLViewWithEditor : NSView
|
@interface GLViewWithEditor : NSView
|
||||||
|
@ -187,36 +187,6 @@ static void CnfThawWindowPos(Gtk::Window *win, const std::string &key) {
|
|||||||
win->resize(w, h);
|
win->resize(w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Timers */
|
|
||||||
|
|
||||||
static bool TimerCallback() {
|
|
||||||
SS.GW.TimerCallback();
|
|
||||||
SS.TW.TimerCallback();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetTimerFor(int milliseconds) {
|
|
||||||
Glib::signal_timeout().connect(&TimerCallback, milliseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool AutosaveTimerCallback() {
|
|
||||||
SS.Autosave();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetAutosaveTimerFor(int minutes) {
|
|
||||||
Glib::signal_timeout().connect(&AutosaveTimerCallback, minutes * 60 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool LaterCallback() {
|
|
||||||
SS.DoLater();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScheduleLater() {
|
|
||||||
Glib::signal_timeout().connect(&LaterCallback, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Editor overlay */
|
/* Editor overlay */
|
||||||
|
|
||||||
class EditorOverlay : public Gtk::Fixed {
|
class EditorOverlay : public Gtk::Fixed {
|
||||||
|
32
src/platform/gui.h
Normal file
32
src/platform/gui.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// An abstraction for platform-dependent GUI functionality.
|
||||||
|
//
|
||||||
|
// Copyright 2018 whitequark
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef SOLVESPACE_GUI_H
|
||||||
|
#define SOLVESPACE_GUI_H
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Interfaces
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// A native single-shot timer.
|
||||||
|
class Timer {
|
||||||
|
public:
|
||||||
|
std::function<void()> onTimeout;
|
||||||
|
|
||||||
|
virtual ~Timer() {}
|
||||||
|
|
||||||
|
virtual void WindUp(unsigned milliseconds) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::unique_ptr<Timer> TimerRef;
|
||||||
|
|
||||||
|
TimerRef CreateTimer();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
40
src/platform/guigtk.cpp
Normal file
40
src/platform/guigtk.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// The GTK-based implementation of platform-dependent GUI functionality.
|
||||||
|
//
|
||||||
|
// Copyright 2018 whitequark
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include <glibmm/main.h>
|
||||||
|
#include "solvespace.h"
|
||||||
|
|
||||||
|
namespace SolveSpace {
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Timers
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TimerImplGtk : public Timer {
|
||||||
|
public:
|
||||||
|
sigc::connection _connection;
|
||||||
|
|
||||||
|
void WindUp(unsigned milliseconds) override {
|
||||||
|
if(!_connection.empty()) {
|
||||||
|
_connection.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto handler = [this]() {
|
||||||
|
if(this->onTimeout) {
|
||||||
|
this->onTimeout();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
_connection = Glib::signal_timeout().connect(handler, milliseconds);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TimerRef CreateTimer() {
|
||||||
|
return std::unique_ptr<TimerImplGtk>(new TimerImplGtk);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
74
src/platform/guimac.mm
Normal file
74
src/platform/guimac.mm
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// The Cocoa-based implementation of platform-dependent GUI functionality.
|
||||||
|
//
|
||||||
|
// Copyright 2018 whitequark
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
#include "solvespace.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Objective-C bridging
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@interface SSFunction : NSObject
|
||||||
|
- (SSFunction *)initWithFunction:(std::function<void ()> *)aFunc;
|
||||||
|
- (void)run;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SSFunction
|
||||||
|
{
|
||||||
|
std::function<void ()> *func;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (SSFunction *)initWithFunction:(std::function<void ()> *)aFunc {
|
||||||
|
if(self = [super init]) {
|
||||||
|
func = aFunc;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)run {
|
||||||
|
if(*func) (*func)();
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
namespace SolveSpace {
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Timers
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TimerImplCocoa : public Timer {
|
||||||
|
public:
|
||||||
|
NSTimer *timer;
|
||||||
|
|
||||||
|
TimerImplCocoa() : timer(NULL) {}
|
||||||
|
|
||||||
|
void WindUp(unsigned milliseconds) override {
|
||||||
|
SSFunction *callback = [[SSFunction alloc] init:&this->onTimeout];
|
||||||
|
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
|
||||||
|
[callback methodSignatureForSelector:@selector(run)]];
|
||||||
|
[invocation setTarget:callback];
|
||||||
|
[invocation setSelector:@selector(run)];
|
||||||
|
|
||||||
|
if(timer != NULL) {
|
||||||
|
[timer invalidate];
|
||||||
|
}
|
||||||
|
timer = [NSTimer scheduledTimerWithTimeInterval:(milliseconds / 1000.0)
|
||||||
|
invocation:invocation repeats:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
~TimerImplCocoa() {
|
||||||
|
if(timer != NULL) {
|
||||||
|
[timer invalidate];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TimerRef CreateTimer() {
|
||||||
|
return std::unique_ptr<TimerImplCocoa>(new TimerImplCocoa);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,23 @@
|
|||||||
|
|
||||||
namespace SolveSpace {
|
namespace SolveSpace {
|
||||||
|
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Timers
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TimerImplDummy : public Timer {
|
||||||
|
public:
|
||||||
|
void WindUp(unsigned milliseconds) override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
TimerRef CreateTimer() {
|
||||||
|
return std::unique_ptr<Timer>(new TimerImplDummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Settings
|
// Settings
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -78,17 +95,6 @@ std::string CnfThawString(const std::string &val, const std::string &key) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Timers
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void SetTimerFor(int milliseconds) {
|
|
||||||
}
|
|
||||||
void SetAutosaveTimerFor(int minutes) {
|
|
||||||
}
|
|
||||||
void ScheduleLater() {
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Rendering
|
// Rendering
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
80
src/platform/guiwin.cpp
Normal file
80
src/platform/guiwin.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// The Win32-based implementation of platform-dependent GUI functionality.
|
||||||
|
//
|
||||||
|
// Copyright 2018 whitequark
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "solvespace.h"
|
||||||
|
// Include after solvespace.h to avoid identifier clashes.
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace SolveSpace {
|
||||||
|
namespace Platform {
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Windows API bridging
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define sscheck(expr) do { \
|
||||||
|
SetLastError(0); \
|
||||||
|
if(!(expr)) \
|
||||||
|
CheckLastError(__FILE__, __LINE__, __func__, #expr); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
void CheckLastError(const char *file, int line, const char *function, const char *expr) {
|
||||||
|
if(GetLastError() != S_OK) {
|
||||||
|
LPWSTR messageW;
|
||||||
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
|
||||||
|
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPWSTR)&messageW, 0, NULL);
|
||||||
|
|
||||||
|
std::string message;
|
||||||
|
message += ssprintf("File %s, line %u, function %s:\n", file, line, function);
|
||||||
|
message += ssprintf("Win32 API call failed: %s.\n", expr);
|
||||||
|
message += ssprintf("Error: %s", Narrow(messageW).c_str());
|
||||||
|
FatalError(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Timers
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TimerImplWin32 : public Timer {
|
||||||
|
public:
|
||||||
|
static HWND WindowHandle() {
|
||||||
|
static HWND hTimerWnd;
|
||||||
|
if(hTimerWnd == NULL) {
|
||||||
|
sscheck(hTimerWnd = CreateWindowExW(0, L"Message", NULL, 0, 0, 0, 0, 0,
|
||||||
|
HWND_MESSAGE, NULL, NULL, NULL));
|
||||||
|
}
|
||||||
|
return hTimerWnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CALLBACK TimerFunc(HWND hwnd, UINT msg, UINT_PTR event, DWORD time) {
|
||||||
|
sscheck(KillTimer(WindowHandle(), event));
|
||||||
|
|
||||||
|
TimerImplWin32 *timer = (TimerImplWin32*)event;
|
||||||
|
if(timer->onTimeout) {
|
||||||
|
timer->onTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindUp(unsigned milliseconds) override {
|
||||||
|
// FIXME(platform/gui): use SetCoalescableTimer when it's available (8+)
|
||||||
|
sscheck(SetTimer(WindowHandle(), (UINT_PTR)this,
|
||||||
|
milliseconds, &TimerImplWin32::TimerFunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
~TimerImplWin32() {
|
||||||
|
// FIXME(platform/gui): there's a race condition here--WM_TIMER messages already
|
||||||
|
// posted to the queue are not removed, so this destructor is at most "best effort".
|
||||||
|
KillTimer(WindowHandle(), (UINT_PTR)this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TimerRef CreateTimer() {
|
||||||
|
return std::unique_ptr<TimerImplWin32>(new TimerImplWin32);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -250,33 +250,6 @@ ContextCommand SolveSpace::ShowContextMenu()
|
|||||||
return (ContextCommand)r;
|
return (ContextCommand)r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CALLBACK TimerCallback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time)
|
|
||||||
{
|
|
||||||
// The timer is periodic, so needs to be killed explicitly.
|
|
||||||
KillTimer(GraphicsWnd, 1);
|
|
||||||
SS.GW.TimerCallback();
|
|
||||||
SS.TW.TimerCallback();
|
|
||||||
}
|
|
||||||
void SolveSpace::SetTimerFor(int milliseconds)
|
|
||||||
{
|
|
||||||
SetTimer(GraphicsWnd, 1, milliseconds, TimerCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SolveSpace::ScheduleLater()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CALLBACK AutosaveCallback(HWND hwnd, UINT msg, UINT_PTR id, DWORD time)
|
|
||||||
{
|
|
||||||
KillTimer(GraphicsWnd, 1);
|
|
||||||
SS.Autosave();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SolveSpace::SetAutosaveTimerFor(int minutes)
|
|
||||||
{
|
|
||||||
SetTimer(GraphicsWnd, 2, minutes * 60 * 1000, AutosaveCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GetWindowSize(HWND hwnd, int *w, int *h)
|
static void GetWindowSize(HWND hwnd, int *w, int *h)
|
||||||
{
|
{
|
||||||
RECT r;
|
RECT r;
|
||||||
@ -1561,13 +1534,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|||||||
while((ret = GetMessage(&msg, NULL, 0, 0)) != 0) {
|
while((ret = GetMessage(&msg, NULL, 0, 0)) != 0) {
|
||||||
#ifdef HAVE_SPACEWARE
|
#ifdef HAVE_SPACEWARE
|
||||||
// Is it a message from the six degree of freedom input device?
|
// Is it a message from the six degree of freedom input device?
|
||||||
if(ProcessSpaceNavigatorMsg(&msg)) goto done;
|
if(ProcessSpaceNavigatorMsg(&msg)) continue;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// A message from the keyboard, which should be processed as a keyboard
|
// A message from the keyboard, which should be processed as a keyboard
|
||||||
// accelerator?
|
// accelerator?
|
||||||
if(msg.message == WM_KEYDOWN) {
|
if(msg.message == WM_KEYDOWN) {
|
||||||
if(ProcessKeyDown(msg.wParam)) goto done;
|
if(ProcessKeyDown(msg.wParam)) continue;
|
||||||
}
|
}
|
||||||
if(msg.message == WM_SYSKEYDOWN && msg.hwnd == TextWnd) {
|
if(msg.message == WM_SYSKEYDOWN && msg.hwnd == TextWnd) {
|
||||||
// If the user presses the Alt key when the text window has focus,
|
// If the user presses the Alt key when the text window has focus,
|
||||||
@ -1578,8 +1551,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|||||||
// None of the above; so just a normal message to process.
|
// None of the above; so just a normal message to process.
|
||||||
TranslateMessage(&msg);
|
TranslateMessage(&msg);
|
||||||
DispatchMessage(&msg);
|
DispatchMessage(&msg);
|
||||||
done:
|
|
||||||
SS.DoLater();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_SPACEWARE
|
#ifdef HAVE_SPACEWARE
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
#define SOLVESPACE_RENDER_H
|
#define SOLVESPACE_RENDER_H
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Interfaces and utilities common for all renderers.
|
// Interfaces common for all renderers
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
enum class StipplePattern : uint32_t;
|
enum class StipplePattern : uint32_t;
|
||||||
@ -313,7 +313,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// 2d renderers.
|
// 2d renderers
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
class CairoRenderer : public SurfaceRenderer {
|
class CairoRenderer : public SurfaceRenderer {
|
||||||
@ -340,7 +340,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// 3d renderers.
|
// 3d renderers
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// An offscreen renderer based on OpenGL framebuffers.
|
// An offscreen renderer based on OpenGL framebuffers.
|
||||||
@ -355,6 +355,10 @@ public:
|
|||||||
void Clear();
|
void Clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Factories
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
std::shared_ptr<ViewportCanvas> CreateRenderer();
|
std::shared_ptr<ViewportCanvas> CreateRenderer();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -110,11 +110,21 @@ void SolveSpaceUI::Init() {
|
|||||||
SetLocale(locale);
|
SetLocale(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timerGenerateAll = Platform::CreateTimer();
|
||||||
|
timerGenerateAll->onTimeout = std::bind(&SolveSpaceUI::GenerateAll, &SS, Generate::DIRTY,
|
||||||
|
/*andFindFree=*/false, /*genForBBox=*/false);
|
||||||
|
|
||||||
|
timerShowTW = Platform::CreateTimer();
|
||||||
|
timerShowTW->onTimeout = std::bind(&TextWindow::Show, &TW);
|
||||||
|
|
||||||
|
timerAutosave = Platform::CreateTimer();
|
||||||
|
timerAutosave->onTimeout = std::bind(&SolveSpaceUI::Autosave, &SS);
|
||||||
|
|
||||||
// The default styles (colors, line widths, etc.) are also stored in the
|
// The default styles (colors, line widths, etc.) are also stored in the
|
||||||
// configuration file, but we will automatically load those as we need
|
// configuration file, but we will automatically load those as we need
|
||||||
// them.
|
// them.
|
||||||
|
|
||||||
SetAutosaveTimerFor(autosaveInterval);
|
ScheduleAutosave();
|
||||||
|
|
||||||
NewFile();
|
NewFile();
|
||||||
AfterNewFile();
|
AfterNewFile();
|
||||||
@ -232,21 +242,15 @@ void SolveSpaceUI::Exit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SolveSpaceUI::ScheduleGenerateAll() {
|
void SolveSpaceUI::ScheduleGenerateAll() {
|
||||||
if(!later.scheduled) ScheduleLater();
|
timerGenerateAll->WindUp(0);
|
||||||
later.scheduled = true;
|
|
||||||
later.generateAll = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SolveSpaceUI::ScheduleShowTW() {
|
void SolveSpaceUI::ScheduleShowTW() {
|
||||||
if(!later.scheduled) ScheduleLater();
|
timerShowTW->WindUp(0);
|
||||||
later.scheduled = true;
|
|
||||||
later.showTW = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SolveSpaceUI::DoLater() {
|
void SolveSpaceUI::ScheduleAutosave() {
|
||||||
if(later.generateAll) GenerateAll();
|
timerAutosave->WindUp(autosaveInterval * 60 * 1000);
|
||||||
if(later.showTW) TW.Show();
|
|
||||||
later = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double SolveSpaceUI::MmPerUnit() {
|
double SolveSpaceUI::MmPerUnit() {
|
||||||
@ -389,14 +393,13 @@ bool SolveSpaceUI::GetFilenameAndSave(bool saveAs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SolveSpaceUI::Autosave()
|
void SolveSpaceUI::Autosave()
|
||||||
{
|
{
|
||||||
SetAutosaveTimerFor(autosaveInterval);
|
ScheduleAutosave();
|
||||||
|
|
||||||
if(!saveFile.IsEmpty() && unsaved)
|
if(!saveFile.IsEmpty() && unsaved) {
|
||||||
return SaveToFile(saveFile.WithExtension(AUTOSAVE_EXT));
|
SaveToFile(saveFile.WithExtension(AUTOSAVE_EXT));
|
||||||
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SolveSpaceUI::RemoveAutosave()
|
void SolveSpaceUI::RemoveAutosave()
|
||||||
|
@ -135,6 +135,7 @@ enum class ContextCommand : uint32_t;
|
|||||||
// From the platform-specific code.
|
// From the platform-specific code.
|
||||||
|
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
|
#include "platform/gui.h"
|
||||||
|
|
||||||
const size_t MAX_RECENT = 8;
|
const size_t MAX_RECENT = 8;
|
||||||
extern Platform::Path RecentFile[MAX_RECENT];
|
extern Platform::Path RecentFile[MAX_RECENT];
|
||||||
@ -202,9 +203,6 @@ void dbp(const char *str, ...);
|
|||||||
void SetCurrentFilename(const Platform::Path &filename);
|
void SetCurrentFilename(const Platform::Path &filename);
|
||||||
void SetMousePointerToHand(bool yes);
|
void SetMousePointerToHand(bool yes);
|
||||||
void DoMessageBox(const char *str, int rows, int cols, bool error);
|
void DoMessageBox(const char *str, int rows, int cols, bool error);
|
||||||
void SetTimerFor(int milliseconds);
|
|
||||||
void SetAutosaveTimerFor(int minutes);
|
|
||||||
void ScheduleLater();
|
|
||||||
void ExitNow();
|
void ExitNow();
|
||||||
|
|
||||||
void CnfFreezeInt(uint32_t val, const std::string &name);
|
void CnfFreezeInt(uint32_t val, const std::string &name);
|
||||||
@ -736,7 +734,7 @@ public:
|
|||||||
Style s;
|
Style s;
|
||||||
} sv;
|
} sv;
|
||||||
static void MenuFile(Command id);
|
static void MenuFile(Command id);
|
||||||
bool Autosave();
|
void Autosave();
|
||||||
void RemoveAutosave();
|
void RemoveAutosave();
|
||||||
bool GetFilenameAndSave(bool saveAs);
|
bool GetFilenameAndSave(bool saveAs);
|
||||||
bool OkayToStartNewFile();
|
bool OkayToStartNewFile();
|
||||||
@ -853,14 +851,12 @@ public:
|
|||||||
// the sketch!
|
// the sketch!
|
||||||
bool allConsistent;
|
bool allConsistent;
|
||||||
|
|
||||||
struct {
|
Platform::TimerRef timerShowTW;
|
||||||
bool scheduled;
|
Platform::TimerRef timerGenerateAll;
|
||||||
bool showTW;
|
Platform::TimerRef timerAutosave;
|
||||||
bool generateAll;
|
|
||||||
} later;
|
|
||||||
void ScheduleShowTW();
|
void ScheduleShowTW();
|
||||||
void ScheduleGenerateAll();
|
void ScheduleGenerateAll();
|
||||||
void DoLater();
|
void ScheduleAutosave();
|
||||||
|
|
||||||
static void MenuHelp(Command id);
|
static void MenuHelp(Command id);
|
||||||
|
|
||||||
|
@ -463,7 +463,9 @@ done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TextWindow::Show() {
|
void TextWindow::Show() {
|
||||||
if(SS.GW.pending.operation == GraphicsWindow::Pending::NONE) SS.GW.ClearPending();
|
if(SS.GW.pending.operation == GraphicsWindow::Pending::NONE) {
|
||||||
|
SS.GW.ClearPending(/*scheduleShowTW=*/false);
|
||||||
|
}
|
||||||
|
|
||||||
SS.GW.GroupSelection();
|
SS.GW.GroupSelection();
|
||||||
auto const &gs = SS.GW.gs;
|
auto const &gs = SS.GW.gs;
|
||||||
@ -519,12 +521,6 @@ void TextWindow::Show() {
|
|||||||
InvalidateText();
|
InvalidateText();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextWindow::TimerCallback()
|
|
||||||
{
|
|
||||||
tooltippedButton = hoveredButton;
|
|
||||||
InvalidateText();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow how,
|
void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow how,
|
||||||
double mx, double my)
|
double mx, double my)
|
||||||
{
|
{
|
||||||
@ -550,13 +546,8 @@ void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow
|
|||||||
button->Draw(uiCanvas, x, y, (button == hoveredButton));
|
button->Draw(uiCanvas, x, y, (button == hoveredButton));
|
||||||
} else if(mx > x - 2 && mx < x + 26 &&
|
} else if(mx > x - 2 && mx < x + 26 &&
|
||||||
my < y + 2 && my > y - 26) {
|
my < y + 2 && my > y - 26) {
|
||||||
// The mouse is hovered over this icon, so do the tooltip
|
if(button != oldHovered) {
|
||||||
// stuff.
|
// FIXME(platorm/gui): implement native tooltips here
|
||||||
if(button != tooltippedButton) {
|
|
||||||
oldMousePos = Point2d::From(mx, my);
|
|
||||||
}
|
|
||||||
if(button != oldHovered || how == CLICK) {
|
|
||||||
SetTimerFor(1000);
|
|
||||||
}
|
}
|
||||||
hoveredButton = button;
|
hoveredButton = button;
|
||||||
if(how == CLICK) {
|
if(how == CLICK) {
|
||||||
@ -570,33 +561,6 @@ void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow
|
|||||||
if(how != PAINT && hoveredButton != oldHovered) {
|
if(how != PAINT && hoveredButton != oldHovered) {
|
||||||
InvalidateText();
|
InvalidateText();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tooltippedButton && !tooltippedButton->Tooltip().empty()) {
|
|
||||||
if(how == PAINT) {
|
|
||||||
std::string tooltip = tooltippedButton->Tooltip();
|
|
||||||
|
|
||||||
int ox = (int)oldMousePos.x, oy = (int)oldMousePos.y - LINE_HEIGHT;
|
|
||||||
ox += 3;
|
|
||||||
oy -= 3;
|
|
||||||
int tw = (tooltip.length() + 1) * (CHAR_WIDTH_ - 1);
|
|
||||||
ox = min(ox, (width - 25) - tw);
|
|
||||||
oy = max(oy, 5);
|
|
||||||
|
|
||||||
uiCanvas->DrawRect(ox, ox+tw, oy, oy+LINE_HEIGHT,
|
|
||||||
/*fillColor=*/{ 255, 255, 150, 255 },
|
|
||||||
/*outlineColor=*/{ 0, 0, 0, 255 },
|
|
||||||
/*zIndex=*/1);
|
|
||||||
uiCanvas->DrawBitmapText(tooltip, ox+5, oy-3+LINE_HEIGHT, { 0, 0, 0, 255 },
|
|
||||||
/*zIndex=*/1);
|
|
||||||
} else {
|
|
||||||
if(!hoveredButton || (hoveredButton != tooltippedButton)) {
|
|
||||||
tooltippedButton = NULL;
|
|
||||||
InvalidateGraphics();
|
|
||||||
}
|
|
||||||
// And if we're hovered, then we've set a timer that will cause
|
|
||||||
// us to show the tool tip later.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@ -1077,7 +1041,6 @@ void TextWindow::MouseEvent(bool leftClick, bool leftDown, double x, double y) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TextWindow::MouseLeave() {
|
void TextWindow::MouseLeave() {
|
||||||
tooltippedButton = NULL;
|
|
||||||
hoveredButton = NULL;
|
hoveredButton = NULL;
|
||||||
hoveredRow = 0;
|
hoveredRow = 0;
|
||||||
hoveredCol = 0;
|
hoveredCol = 0;
|
||||||
|
@ -98,22 +98,11 @@ bool GraphicsWindow::ToolbarMouseMoved(int x, int y) {
|
|||||||
bool withinToolbar = ToolbarDrawOrHitTest(x, y, NULL, &nh);
|
bool withinToolbar = ToolbarDrawOrHitTest(x, y, NULL, &nh);
|
||||||
if(!withinToolbar) nh = Command::NONE;
|
if(!withinToolbar) nh = Command::NONE;
|
||||||
|
|
||||||
if(nh != toolbarTooltipped) {
|
|
||||||
// Don't let the tool tip move around if the mouse moves within the
|
|
||||||
// same item.
|
|
||||||
toolbarMouseX = x;
|
|
||||||
toolbarMouseY = y;
|
|
||||||
toolbarTooltipped = Command::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nh != toolbarHovered) {
|
if(nh != toolbarHovered) {
|
||||||
toolbarHovered = nh;
|
toolbarHovered = nh;
|
||||||
SetTimerFor(1000);
|
// FIXME(platorm/gui): implement native tooltips here
|
||||||
PaintGraphics();
|
PaintGraphics();
|
||||||
}
|
}
|
||||||
// So if we moved off the toolbar, then toolbarHovered is now equal to
|
|
||||||
// zero, so it doesn't matter if the tool tip timer expires. And if
|
|
||||||
// we moved from one item to another, we reset the timer, so also okay.
|
|
||||||
return withinToolbar;
|
return withinToolbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +127,6 @@ bool GraphicsWindow::ToolbarMouseDown(int x, int y) {
|
|||||||
bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
|
bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
|
||||||
UiCanvas *canvas, Command *menuHit)
|
UiCanvas *canvas, Command *menuHit)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
int x = 17, y = (int)(height - 52);
|
int x = 17, y = (int)(height - 52);
|
||||||
|
|
||||||
int fudge = 8;
|
int fudge = 8;
|
||||||
@ -159,9 +147,6 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
|
|||||||
/*outlineColor=*/{});
|
/*outlineColor=*/{});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool showTooltip = false;
|
|
||||||
std::string tooltip;
|
|
||||||
|
|
||||||
bool leftpos = true;
|
bool leftpos = true;
|
||||||
for(ToolIcon &icon : Toolbar) {
|
for(ToolIcon &icon : Toolbar) {
|
||||||
if(icon.name == "") { // spacer
|
if(icon.name == "") { // spacer
|
||||||
@ -201,14 +186,6 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
|
|||||||
/*fillColor=*/{ 255, 255, 0, 75 },
|
/*fillColor=*/{ 255, 255, 0, 75 },
|
||||||
/*outlineColor=*/{});
|
/*outlineColor=*/{});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(toolbarTooltipped == icon.command) {
|
|
||||||
// Display the tool tip for this item; postpone till later
|
|
||||||
// so that no one draws over us. Don't need position since
|
|
||||||
// that's just wherever the mouse is.
|
|
||||||
showTooltip = true;
|
|
||||||
tooltip = Translate(icon.tooltip);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
int boxhw = 16;
|
int boxhw = 16;
|
||||||
if(mx < (x+boxhw) && mx > (x - boxhw) &&
|
if(mx < (x+boxhw) && mx > (x - boxhw) &&
|
||||||
@ -228,35 +205,5 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(canvas) {
|
|
||||||
// Do this last so that nothing can draw over it.
|
|
||||||
if(showTooltip) {
|
|
||||||
for(i = 0; SS.GW.menu[i].level >= 0; i++) {
|
|
||||||
if(toolbarTooltipped == SS.GW.menu[i].id) {
|
|
||||||
std::string accel = MakeAcceleratorLabel(SS.GW.menu[i].accel);
|
|
||||||
if(!accel.empty()) {
|
|
||||||
tooltip += ssprintf(" (%s)", accel.c_str());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int tw = (int)canvas->canvas->GetBitmapFont()->GetWidth(tooltip) * 8 + 10,
|
|
||||||
th = SS.TW.LINE_HEIGHT + 2;
|
|
||||||
|
|
||||||
int ox = toolbarMouseX + 3, oy = toolbarMouseY + 3;
|
|
||||||
canvas->DrawRect(ox, ox+tw, oy, oy+th,
|
|
||||||
/*fillColor=*/{ 255, 255, 150, 255 },
|
|
||||||
/*outlineColor=*/{ 0, 0, 0, 255 });
|
|
||||||
canvas->DrawBitmapText(tooltip, ox+5, oy+4, { 0, 0, 0, 255 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return withinToolbar;
|
return withinToolbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsWindow::TimerCallback() {
|
|
||||||
SS.GW.toolbarTooltipped = SS.GW.toolbarHovered;
|
|
||||||
PaintGraphics();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
9
src/ui.h
9
src/ui.h
@ -326,9 +326,7 @@ public:
|
|||||||
};
|
};
|
||||||
void DrawOrHitTestIcons(UiCanvas *canvas, DrawOrHitHow how,
|
void DrawOrHitTestIcons(UiCanvas *canvas, DrawOrHitHow how,
|
||||||
double mx, double my);
|
double mx, double my);
|
||||||
void TimerCallback();
|
Button *hoveredButton;
|
||||||
Point2d oldMousePos;
|
|
||||||
Button *hoveredButton, *tooltippedButton;
|
|
||||||
|
|
||||||
Vector HsvToRgb(Vector hsv);
|
Vector HsvToRgb(Vector hsv);
|
||||||
std::shared_ptr<Pixmap> HsvPattern2d(int w, int h);
|
std::shared_ptr<Pixmap> HsvPattern2d(int w, int h);
|
||||||
@ -717,7 +715,7 @@ public:
|
|||||||
bool hasSuggestion;
|
bool hasSuggestion;
|
||||||
Constraint::Type suggestion;
|
Constraint::Type suggestion;
|
||||||
} pending;
|
} pending;
|
||||||
void ClearPending();
|
void ClearPending(bool scheduleShowTW = true);
|
||||||
bool IsFromPending(hRequest r);
|
bool IsFromPending(hRequest r);
|
||||||
void AddToPending(hRequest r);
|
void AddToPending(hRequest r);
|
||||||
void ReplacePending(hRequest before, hRequest after);
|
void ReplacePending(hRequest before, hRequest after);
|
||||||
@ -839,10 +837,7 @@ public:
|
|||||||
void ToolbarDraw(UiCanvas *canvas);
|
void ToolbarDraw(UiCanvas *canvas);
|
||||||
bool ToolbarMouseMoved(int x, int y);
|
bool ToolbarMouseMoved(int x, int y);
|
||||||
bool ToolbarMouseDown(int x, int y);
|
bool ToolbarMouseDown(int x, int y);
|
||||||
static void TimerCallback();
|
|
||||||
Command toolbarHovered;
|
Command toolbarHovered;
|
||||||
Command toolbarTooltipped;
|
|
||||||
int toolbarMouseX, toolbarMouseY;
|
|
||||||
|
|
||||||
// This sets what gets displayed.
|
// This sets what gets displayed.
|
||||||
bool showWorkplanes;
|
bool showWorkplanes;
|
||||||
|
Loading…
Reference in New Issue
Block a user