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:
whitequark 2018-07-11 05:35:31 +00:00
parent 28f94dcd0a
commit 7ab87caa88
17 changed files with 295 additions and 257 deletions

View File

@ -105,6 +105,7 @@ endif()
if(WIN32)
set(platform_SOURCES
platform/w32main.cpp
platform/guiwin.cpp
${gl_SOURCES})
set(platform_LIBRARIES
@ -116,12 +117,13 @@ elseif(APPLE)
set(platform_SOURCES
platform/cocoamain.mm
platform/guimac.mm
render/rendergl.cpp
${gl_SOURCES})
else()
set(platform_SOURCES
platform/gtkmain.cpp
render/rendergl.cpp
platform/guigtk.cpp
${gl_SOURCES})
set(platform_LIBRARIES
@ -333,7 +335,7 @@ endif()
# solvespace headless library
set(headless_SOURCES
platform/headless.cpp
platform/guinone.cpp
render/rendercairo.cpp)
add_library(solvespace-headless STATIC EXCLUDE_FROM_ALL

View File

@ -459,7 +459,7 @@ bool TextWindow::EditControlDoneForConfiguration(const char *s) {
if(sscanf(s, "%d", &interval)==1) {
if(interval >= 1) {
SS.autosaveInterval = interval;
SetAutosaveTimerFor(interval);
SS.ScheduleAutosave();
} else {
Error(_("Bad value: autosave interval should be positive"));
}

View File

@ -468,12 +468,14 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
}
}
void GraphicsWindow::ClearPending() {
void GraphicsWindow::ClearPending(bool scheduleShowTW) {
pending.points.Clear();
pending.requests.Clear();
pending = {};
if(scheduleShowTW) {
SS.ScheduleShowTW();
}
}
bool GraphicsWindow::IsFromPending(hRequest r) {
for(auto &req : pending.requests) {
@ -1536,7 +1538,6 @@ void GraphicsWindow::MouseLeave() {
// currently a context menu shown.
if(!context.active) {
hover.Clear();
toolbarTooltipped = Command::NONE;
toolbarHovered = Command::NONE;
PaintGraphics();
}

View File

@ -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 */
@interface GLViewWithEditor : NSView

View File

@ -187,36 +187,6 @@ static void CnfThawWindowPos(Gtk::Window *win, const std::string &key) {
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 */
class EditorOverlay : public Gtk::Fixed {

32
src/platform/gui.h Normal file
View 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
View 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
View 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);
}
}
}

View File

@ -8,6 +8,23 @@
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
//-----------------------------------------------------------------------------
@ -78,17 +95,6 @@ std::string CnfThawString(const std::string &val, const std::string &key) {
return ret;
}
//-----------------------------------------------------------------------------
// Timers
//-----------------------------------------------------------------------------
void SetTimerFor(int milliseconds) {
}
void SetAutosaveTimerFor(int minutes) {
}
void ScheduleLater() {
}
//-----------------------------------------------------------------------------
// Rendering
//-----------------------------------------------------------------------------

80
src/platform/guiwin.cpp Normal file
View 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);
}
}
}

View File

@ -250,33 +250,6 @@ ContextCommand SolveSpace::ShowContextMenu()
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)
{
RECT r;
@ -1561,13 +1534,13 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
while((ret = GetMessage(&msg, NULL, 0, 0)) != 0) {
#ifdef HAVE_SPACEWARE
// Is it a message from the six degree of freedom input device?
if(ProcessSpaceNavigatorMsg(&msg)) goto done;
if(ProcessSpaceNavigatorMsg(&msg)) continue;
#endif
// A message from the keyboard, which should be processed as a keyboard
// accelerator?
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 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.
TranslateMessage(&msg);
DispatchMessage(&msg);
done:
SS.DoLater();
}
#ifdef HAVE_SPACEWARE

View File

@ -8,7 +8,7 @@
#define SOLVESPACE_RENDER_H
//-----------------------------------------------------------------------------
// Interfaces and utilities common for all renderers.
// Interfaces common for all renderers
//-----------------------------------------------------------------------------
enum class StipplePattern : uint32_t;
@ -313,7 +313,7 @@ public:
};
//-----------------------------------------------------------------------------
// 2d renderers.
// 2d renderers
//-----------------------------------------------------------------------------
class CairoRenderer : public SurfaceRenderer {
@ -340,7 +340,7 @@ public:
};
//-----------------------------------------------------------------------------
// 3d renderers.
// 3d renderers
//-----------------------------------------------------------------------------
// An offscreen renderer based on OpenGL framebuffers.
@ -355,6 +355,10 @@ public:
void Clear();
};
//-----------------------------------------------------------------------------
// Factories
//-----------------------------------------------------------------------------
std::shared_ptr<ViewportCanvas> CreateRenderer();
#endif

View File

@ -110,11 +110,21 @@ void SolveSpaceUI::Init() {
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
// configuration file, but we will automatically load those as we need
// them.
SetAutosaveTimerFor(autosaveInterval);
ScheduleAutosave();
NewFile();
AfterNewFile();
@ -232,21 +242,15 @@ void SolveSpaceUI::Exit() {
}
void SolveSpaceUI::ScheduleGenerateAll() {
if(!later.scheduled) ScheduleLater();
later.scheduled = true;
later.generateAll = true;
timerGenerateAll->WindUp(0);
}
void SolveSpaceUI::ScheduleShowTW() {
if(!later.scheduled) ScheduleLater();
later.scheduled = true;
later.showTW = true;
timerShowTW->WindUp(0);
}
void SolveSpaceUI::DoLater() {
if(later.generateAll) GenerateAll();
if(later.showTW) TW.Show();
later = {};
void SolveSpaceUI::ScheduleAutosave() {
timerAutosave->WindUp(autosaveInterval * 60 * 1000);
}
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)
return SaveToFile(saveFile.WithExtension(AUTOSAVE_EXT));
return false;
if(!saveFile.IsEmpty() && unsaved) {
SaveToFile(saveFile.WithExtension(AUTOSAVE_EXT));
}
}
void SolveSpaceUI::RemoveAutosave()

View File

@ -135,6 +135,7 @@ enum class ContextCommand : uint32_t;
// From the platform-specific code.
#include "platform/platform.h"
#include "platform/gui.h"
const size_t MAX_RECENT = 8;
extern Platform::Path RecentFile[MAX_RECENT];
@ -202,9 +203,6 @@ void dbp(const char *str, ...);
void SetCurrentFilename(const Platform::Path &filename);
void SetMousePointerToHand(bool yes);
void DoMessageBox(const char *str, int rows, int cols, bool error);
void SetTimerFor(int milliseconds);
void SetAutosaveTimerFor(int minutes);
void ScheduleLater();
void ExitNow();
void CnfFreezeInt(uint32_t val, const std::string &name);
@ -736,7 +734,7 @@ public:
Style s;
} sv;
static void MenuFile(Command id);
bool Autosave();
void Autosave();
void RemoveAutosave();
bool GetFilenameAndSave(bool saveAs);
bool OkayToStartNewFile();
@ -853,14 +851,12 @@ public:
// the sketch!
bool allConsistent;
struct {
bool scheduled;
bool showTW;
bool generateAll;
} later;
Platform::TimerRef timerShowTW;
Platform::TimerRef timerGenerateAll;
Platform::TimerRef timerAutosave;
void ScheduleShowTW();
void ScheduleGenerateAll();
void DoLater();
void ScheduleAutosave();
static void MenuHelp(Command id);

View File

@ -463,7 +463,9 @@ done:
}
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();
auto const &gs = SS.GW.gs;
@ -519,12 +521,6 @@ void TextWindow::Show() {
InvalidateText();
}
void TextWindow::TimerCallback()
{
tooltippedButton = hoveredButton;
InvalidateText();
}
void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow how,
double mx, double my)
{
@ -550,13 +546,8 @@ void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow
button->Draw(uiCanvas, x, y, (button == hoveredButton));
} else if(mx > x - 2 && mx < x + 26 &&
my < y + 2 && my > y - 26) {
// The mouse is hovered over this icon, so do the tooltip
// stuff.
if(button != tooltippedButton) {
oldMousePos = Point2d::From(mx, my);
}
if(button != oldHovered || how == CLICK) {
SetTimerFor(1000);
if(button != oldHovered) {
// FIXME(platorm/gui): implement native tooltips here
}
hoveredButton = button;
if(how == CLICK) {
@ -570,33 +561,6 @@ void TextWindow::DrawOrHitTestIcons(UiCanvas *uiCanvas, TextWindow::DrawOrHitHow
if(how != PAINT && hoveredButton != oldHovered) {
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() {
tooltippedButton = NULL;
hoveredButton = NULL;
hoveredRow = 0;
hoveredCol = 0;

View File

@ -98,22 +98,11 @@ bool GraphicsWindow::ToolbarMouseMoved(int x, int y) {
bool withinToolbar = ToolbarDrawOrHitTest(x, y, NULL, &nh);
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) {
toolbarHovered = nh;
SetTimerFor(1000);
// FIXME(platorm/gui): implement native tooltips here
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;
}
@ -138,7 +127,6 @@ bool GraphicsWindow::ToolbarMouseDown(int x, int y) {
bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
UiCanvas *canvas, Command *menuHit)
{
int i;
int x = 17, y = (int)(height - 52);
int fudge = 8;
@ -159,9 +147,6 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
/*outlineColor=*/{});
}
bool showTooltip = false;
std::string tooltip;
bool leftpos = true;
for(ToolIcon &icon : Toolbar) {
if(icon.name == "") { // spacer
@ -201,14 +186,6 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
/*fillColor=*/{ 255, 255, 0, 75 },
/*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 {
int boxhw = 16;
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;
}
void GraphicsWindow::TimerCallback() {
SS.GW.toolbarTooltipped = SS.GW.toolbarHovered;
PaintGraphics();
}

View File

@ -326,9 +326,7 @@ public:
};
void DrawOrHitTestIcons(UiCanvas *canvas, DrawOrHitHow how,
double mx, double my);
void TimerCallback();
Point2d oldMousePos;
Button *hoveredButton, *tooltippedButton;
Button *hoveredButton;
Vector HsvToRgb(Vector hsv);
std::shared_ptr<Pixmap> HsvPattern2d(int w, int h);
@ -717,7 +715,7 @@ public:
bool hasSuggestion;
Constraint::Type suggestion;
} pending;
void ClearPending();
void ClearPending(bool scheduleShowTW = true);
bool IsFromPending(hRequest r);
void AddToPending(hRequest r);
void ReplacePending(hRequest before, hRequest after);
@ -839,10 +837,7 @@ public:
void ToolbarDraw(UiCanvas *canvas);
bool ToolbarMouseMoved(int x, int y);
bool ToolbarMouseDown(int x, int y);
static void TimerCallback();
Command toolbarHovered;
Command toolbarTooltipped;
int toolbarMouseX, toolbarMouseY;
// This sets what gets displayed.
bool showWorkplanes;