Replace ugly text links to hide/show things with icons. So add code

to draw those, and hit test with the mouse, and display tool tips
when the user hovers with the mouse. Also, underline links only
when they're hovered, and not otherwise.

And add a separate menu option to align the view to the active
workplane, vs. activating the active group's workplane, and
remap the bottom two graphics window toolbar icons to that and
"nearest iso view" instead of draw in 2d/3d, since people tended
to click on those without understanding and cause trouble.

And by default, we force a parallel projection; so the factory
default camera tangent is now 0.3, not 0.

[git-p4: depot-paths = "//depot/solvespace/": change = 2131]
This commit is contained in:
Jonathan Westhues 2010-05-02 21:04:42 -08:00
parent c4b442f92f
commit 0246add3e9
24 changed files with 435 additions and 155 deletions

View File

@ -97,5 +97,5 @@ toolbar.cpp: $(OBJDIR)/icons.h
glhelper.cpp: bitmapfont.table font.table
$(OBJDIR)/icons.h: icons/* png2c.pl
perl png2c.pl > $(OBJDIR)/icons.h
perl png2c.pl $(OBJDIR)/icons.h $(OBJDIR)/icons-proto.h

View File

@ -359,7 +359,7 @@ void TextWindow::ShowPasteTransformed(void) {
Printf(true, "%Ba %FtREPEAT%E %d time%s %Fl%Lt%f[change]%E",
shown.paste.times, (shown.paste.times == 1) ? "" : "s",
&ScreenChangePasteTransformed);
Printf(false, "%Bd %FtROTATE%E %@° %Fl%Lr%f[change]%E",
Printf(false, "%Bd %FtROTATE%E %@ degrees %Fl%Lr%f[change]%E",
shown.paste.theta*180/PI,
&ScreenChangePasteTransformed);
Printf(false, "%Ba %FtABOUT PT%E (%s, %s, %s) %Fl%Lo%f[use selected]%E",

View File

@ -454,11 +454,13 @@ Vector GraphicsWindow::VectorFromProjs(Vector rightUpForward) {
return r;
}
void GraphicsWindow::Paint(int w, int h) {
void GraphicsWindow::Paint(void) {
int i;
havePainted = true;
width = w; height = h;
int w, h;
GetGraphicsWindowSize(&w, &h);
width = w; height = h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);

View File

@ -716,7 +716,7 @@ void SolveSpace::ExportAsPngTo(char *filename) {
// so repaint the scene. And hide the toolbar too.
int prevShowToolbar = SS.showToolbar;
SS.showToolbar = false;
SS.GW.Paint(w, h);
SS.GW.Paint();
SS.showToolbar = prevShowToolbar;
FILE *f = fopen(filename, "wb");

View File

@ -200,20 +200,10 @@ void SolveSpace::GenerateAll(int first, int last, bool andFindFree) {
double left = 80, top = -20, width = 240, height = 24;
glColor3d(0.9, 0.8, 0.8);
glBegin(GL_QUADS);
glVertex2d(left, top);
glVertex2d(left+width, top);
glVertex2d(left+width, top-height);
glVertex2d(left, top-height);
glEnd();
glxAxisAlignedQuad(left, left+width, top, top-height);
glLineWidth(1);
glColor3d(0.0, 0.0, 0.0);
glBegin(GL_LINE_LOOP);
glVertex2d(left, top);
glVertex2d(left+width, top);
glVertex2d(left+width, top-height);
glVertex2d(left, top-height);
glEnd();
glxAxisAlignedLineLoop(left, left+width, top, top-height);
glxCreateBitmapFont();
glColor3d(0, 0, 0);

View File

@ -10,7 +10,8 @@ static bool ColorLocked;
static bool DepthOffsetLocked;
#define FONT_SCALE(h) ((h)/22.0)
double glxStrWidth(char *str, double h) {
double glxStrWidth(char *str, double h)
{
int w = 0;
for(; *str; str++) {
int c = *str;
@ -21,7 +22,8 @@ double glxStrWidth(char *str, double h) {
}
return w*FONT_SCALE(h)/SS.GW.scale;
}
double glxStrHeight(double h) {
double glxStrHeight(double h)
{
// The characters have height ~22, as they appear in the table.
return 22.0*FONT_SCALE(h)/SS.GW.scale;
}
@ -99,6 +101,26 @@ void glxVertex3v(Vector u)
glVertex3f((GLfloat)u.x, (GLfloat)u.y, (GLfloat)u.z);
}
void glxAxisAlignedQuad(double l, double r, double t, double b)
{
glBegin(GL_QUADS);
glVertex2d(l, t);
glVertex2d(l, b);
glVertex2d(r, b);
glVertex2d(r, t);
glEnd();
}
void glxAxisAlignedLineLoop(double l, double r, double t, double b)
{
glBegin(GL_LINE_LOOP);
glVertex2d(l, t);
glVertex2d(l, b);
glVertex2d(r, b);
glVertex2d(r, t);
glEnd();
}
static void FatLineEndcap(Vector p, Vector u, Vector v)
{
// A table of cos and sin of (pi*i/10 + pi/2), as i goes from 0 to 10
@ -123,7 +145,8 @@ static void FatLineEndcap(Vector p, Vector u, Vector v)
glEnd();
}
void glxFatLine(Vector a, Vector b, double width) {
void glxFatLine(Vector a, Vector b, double width)
{
// The half-width of the line we're drawing.
double hw = width / 2;
Vector ab = b.Minus(a);
@ -277,10 +300,10 @@ void glxFillMesh(int specColor, SMesh *m, DWORD h, DWORD s1, DWORD s2)
glEnd();
}
static void GLX_CALLBACK Vertex(Vector *p) {
static void GLX_CALLBACK Vertex(Vector *p)
{
glxVertex3v(*p);
}
void glxFillPolygon(SPolygon *p)
{
GLUtesselator *gt = gluNewTess();
@ -434,7 +457,8 @@ void glxMarkPolygonNormal(SPolygon *p)
glEnable(GL_LIGHTING);
}
void glxDepthRangeOffset(int units) {
void glxDepthRangeOffset(int units)
{
if(!DepthOffsetLocked) {
// The size of this step depends on the resolution of the Z buffer; for
// a 16-bit buffer, this should be fine.
@ -443,7 +467,8 @@ void glxDepthRangeOffset(int units) {
}
}
void glxDepthRangeLockToFront(bool yes) {
void glxDepthRangeLockToFront(bool yes)
{
if(yes) {
DepthOffsetLocked = true;
glDepthRange(0, 0);
@ -453,7 +478,8 @@ void glxDepthRangeLockToFront(bool yes) {
}
}
void glxCreateBitmapFont(void) {
void glxCreateBitmapFont(void)
{
glBindTexture(GL_TEXTURE_2D, TEXTURE_BITMAP_FONT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@ -467,7 +493,8 @@ void glxCreateBitmapFont(void) {
FontTexture);
}
void glxBitmapCharQuad(char c, double x, double y) {
void glxBitmapCharQuad(char c, double x, double y)
{
int w = SS.TW.CHAR_WIDTH,
h = SS.TW.CHAR_HEIGHT;
@ -491,7 +518,8 @@ void glxBitmapCharQuad(char c, double x, double y) {
}
}
void glxBitmapText(char *str, Vector p) {
void glxBitmapText(char *str, Vector p)
{
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
while(*str) {
@ -504,3 +532,45 @@ void glxBitmapText(char *str, Vector p) {
glDisable(GL_TEXTURE_2D);
}
void glxDrawPixelsWithTexture(BYTE *data, int w, int h)
{
#define MAX_DIM 32
static BYTE Texture[MAX_DIM*MAX_DIM*3];
int i, j;
if(w > MAX_DIM || h > MAX_DIM) oops();
for(i = 0; i < w; i++) {
for(j = 0; j < h; j++) {
Texture[(j*MAX_DIM + i)*3 + 0] = data[(j*w + i)*3 + 0];
Texture[(j*MAX_DIM + i)*3 + 1] = data[(j*w + i)*3 + 1];
Texture[(j*MAX_DIM + i)*3 + 2] = data[(j*w + i)*3 + 2];
}
}
glBindTexture(GL_TEXTURE_2D, TEXTURE_DRAW_PIXELS);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, MAX_DIM, MAX_DIM, 0,
GL_RGB, GL_UNSIGNED_BYTE, Texture);
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glTexCoord2d(0, 0);
glVertex2d(0, h);
glTexCoord2d(((double)w)/MAX_DIM, 0);
glVertex2d(w, h);
glTexCoord2d(((double)w)/MAX_DIM, ((double)h)/MAX_DIM);
glVertex2d(w, 0);
glTexCoord2d(0, ((double)h)/MAX_DIM);
glVertex2d(0, 0);
glEnd();
glDisable(GL_TEXTURE_2D);
}

View File

@ -52,13 +52,14 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{ 1, "Zoom &Out\t-", MNU_ZOOM_OUT, '-', mView },
{ 1, "Zoom To &Fit\tF", MNU_ZOOM_TO_FIT, 'F', mView },
{ 1, NULL, 0, NULL },
{ 1, "Show Snap &Grid\t>", MNU_SHOW_GRID, '.'|S, mView },
{ 1, "Force &Parallel Projection\t`", MNU_PARALLEL_PROJ, '`', mView },
{ 1, NULL, 0, NULL },
{ 1, "Align View to &Workplane\tW", MNU_ONTO_WORKPLANE, 'W', mView },
{ 1, "Nearest &Ortho View\tF2", MNU_NEAREST_ORTHO, F(2), mView },
{ 1, "Nearest &Isometric View\tF3", MNU_NEAREST_ISO, F(3), mView },
{ 1, "&Center View At Point\tF4", MNU_CENTER_VIEW, F(4), mView },
{ 1, NULL, 0, NULL },
{ 1, "Show Snap &Grid\t>", MNU_SHOW_GRID, '.'|S, mView },
{ 1, "Force &Parallel Projection\t`", MNU_PARALLEL_PROJ, '`', mView },
{ 1, NULL, 0, NULL },
{ 1, "Show Text &Window\tTab", MNU_SHOW_TEXT_WND, '\t', mView },
{ 1, "Show &Toolbar", MNU_SHOW_TOOLBAR, 0, mView },
{ 1, NULL, 0, NULL },
@ -81,7 +82,7 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
{11, "Import Recent", MNU_GROUP_RECENT, 0, mGrp },
{ 0, "&Sketch", 0, NULL },
{ 1, "In &Workplane\tW", MNU_SEL_WORKPLANE, 'W', mReq },
{ 1, "In &Workplane\t2", MNU_SEL_WORKPLANE, '2', mReq },
{ 1, "Anywhere In &3d\t3", MNU_FREE_IN_3D, '3', mReq },
{ 1, NULL, 0, NULL },
{ 1, "Datum &Point\tP", MNU_DATUM_POINT, 'P', mReq },
@ -373,6 +374,16 @@ void GraphicsWindow::MenuView(int id) {
InvalidateGraphics();
break;
case MNU_ONTO_WORKPLANE:
if(!SS.GW.LockedInWorkplane()) {
Error("No workplane is active.");
break;
}
SS.GW.AnimateOntoWorkplane();
SS.GW.ClearSuper();
SS.later.showTW = true;
break;
case MNU_NEAREST_ORTHO:
case MNU_NEAREST_ISO: {
static const Vector ortho[3] = {
@ -813,11 +824,15 @@ void GraphicsWindow::MenuRequest(int id) {
} else if(g->type == Group::DRAWING_WORKPLANE) {
// The group's default workplane
g->activeWorkplane = g->h.entity(0);
Message("No workplane selected. Activating default workplane "
"for this group.");
}
if(!SS.GW.LockedInWorkplane()) {
Error("Select workplane (e.g., the XY plane) "
"before locking on.");
Error("No workplane is selected, and the active group does "
"not have a default workplane. Try selecting a "
"workplane, or activating a sketch-in-new-workplane "
"group.");
break;
}
// Align the view with the selected workplane
@ -892,17 +907,16 @@ void GraphicsWindow::ClearSuper(void) {
EnsureValidActives();
}
void GraphicsWindow::ToggleBool(int link, DWORD v) {
bool *vb = (bool *)v;
*vb = !*vb;
void GraphicsWindow::ToggleBool(bool *v) {
*v = !*v;
// The faces are shown as special stippling on the shaded triangle mesh,
// so not meaningful to show them and hide the shaded.
if(!SS.GW.showShaded) SS.GW.showFaces = false;
if(!showShaded) showFaces = false;
// We might need to regenerate the mesh and edge list, since the edges
// wouldn't have been generated if they were previously hidden.
if(SS.GW.showEdges) (SK.GetGroup(SS.GW.activeGroup))->displayDirty = true;
if(showEdges) (SK.GetGroup(activeGroup))->displayDirty = true;
SS.GenerateAll();
InvalidateGraphics();

BIN
icons/constraint.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
icons/edges.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
icons/faces.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

BIN
icons/hidden-lines.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
icons/mesh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
icons/normal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
icons/shaded.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

BIN
icons/workplane.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

View File

@ -2,6 +2,10 @@
use GD;
my ($out, $proto) = @ARGV;
open(OUT, ">$out") or die "$out: $!";
open(PROTO, ">$proto") or die "$proto: $!";
for $file (<icons/*.png>) {
$file =~ m#.*/(.*)\.png#;
@ -17,7 +21,8 @@ for $file (<icons/*.png>) {
($width, $height) = $img->getBounds();
die "$file: $width, $height" if ($width != 24) or ($height != 24);
print "unsigned char $base\[24*24*3] = {\n";
print PROTO "extern unsigned char $base\[24*24*3\];";
print OUT "unsigned char $base\[24*24*3] = {\n";
for($y = 0; $y < 24; $y++) {
for($x = 0; $x < 24; $x++) {
@ -26,10 +31,10 @@ for $file (<icons/*.png>) {
if($r + $g + $b < 11) {
($r, $g, $b) = (30, 30, 30);
}
printf " 0x%02x, 0x%02x, 0x%02x,\n", $r, $g, $b;
printf OUT " 0x%02x, 0x%02x, 0x%02x,\n", $r, $g, $b;
}
}
print "};\n\n";
print OUT "};\n\n";
}

View File

@ -63,7 +63,7 @@ void SolveSpace::Init(char *cmdLine) {
// View units
viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits");
// Camera tangent (determines perspective)
cameraTangent = CnfThawFloat(0.0f, "CameraTangent");
cameraTangent = CnfThawFloat(0.3f, "CameraTangent");
// Grid spacing
gridSpacing = CnfThawFloat(5.0f, "GridSpacing");
// Export scale factor
@ -114,6 +114,10 @@ void SolveSpace::Init(char *cmdLine) {
// configuration file, but we will automatically load those as we need
// them.
// The factory default settings include a non-zero perspective factor,
// but we'll default to that off.
forceParallelProj = true;
// Start with either an empty file, or the file specified on the
// command line.
NewFile();

View File

@ -144,6 +144,7 @@ void InvalidateText(void);
void InvalidateGraphics(void);
void PaintGraphics(void);
void GetGraphicsWindowSize(int *w, int *h);
void GetTextWindowSize(int *w, int *h);
SDWORD GetMilliseconds(void);
SQWORD GetUnixTime(void);
@ -197,6 +198,8 @@ typedef IdList<Param,hParam> ParamList;
// Utility functions that are provided in the platform-independent code.
void glxVertex3v(Vector u);
void glxAxisAlignedQuad(double l, double r, double t, double b);
void glxAxisAlignedLineLoop(double l, double r, double t, double b);
#define DEFAULT_TEXT_HEIGHT (11.5)
#define GLX_CALLBACK __stdcall
typedef void GLX_CALLBACK glxCallbackFptr(void);
@ -221,11 +224,13 @@ void glxColorRGB(DWORD rgb);
void glxColorRGBa(DWORD rgb, double a);
void glxDepthRangeOffset(int units);
void glxDepthRangeLockToFront(bool yes);
void glxDrawPixelsWithTexture(BYTE *data, int w, int h);
void glxCreateBitmapFont(void);
void glxBitmapText(char *str, Vector p);
void glxBitmapCharQuad(char c, double x, double y);
#define TEXTURE_BACKGROUND_IMG 10
#define TEXTURE_BITMAP_FONT 20
#define TEXTURE_DRAW_PIXELS 30
#define arraylen(x) (sizeof((x))/sizeof((x)[0]))

View File

@ -10,42 +10,26 @@ void TextWindow::ScreenHome(int link, DWORD v) {
void TextWindow::ShowHeader(bool withNav) {
ClearScreen();
char *cd = SS.GW.LockedInWorkplane() ?
SK.GetEntity(SS.GW.ActiveWorkplane())->DescriptionString() :
"free in 3d";
char cd[1024], cd2[1024];
if(SS.GW.LockedInWorkplane()) {
sprintf(cd, "in plane: ");
strcpy(cd2, SK.GetEntity(SS.GW.ActiveWorkplane())->DescriptionString());
} else {
sprintf(cd, "drawing / constraining in 3d");
strcpy(cd2, "");
}
// Navigation buttons
if(withNav) {
Printf(false, " %Fl%Lh%fhome%E %Bt%Ft wrkpl:%Fd %s",
(&TextWindow::ScreenHome),
cd);
Printf(false, " %Fl%Lh%fhome%E %Ft%s%E%s",
(&TextWindow::ScreenHome), cd, cd2);
} else {
Printf(false, " %Bt%Ft wrkpl:%Fd %s", cd);
Printf(false, " %Ft%s%E%s", cd, cd2);
}
#define hs(b) ((b) ? 's' : 'h')
Printf(false, "%Bt%Ftshow: "
"%Fp%Ll%D%fwrkpls%E "
"%Fp%Ll%D%fnormals%E "
"%Fp%Ll%D%fpoints%E "
"%Fp%Ll%D%fconstraints%E ",
hs(SS.GW.showWorkplanes), (DWORD)&(SS.GW.showWorkplanes), &(SS.GW.ToggleBool),
hs(SS.GW.showNormals), (DWORD)&(SS.GW.showNormals), &(SS.GW.ToggleBool),
hs(SS.GW.showPoints), (DWORD)&(SS.GW.showPoints), &(SS.GW.ToggleBool),
hs(SS.GW.showConstraints), (DWORD)(&SS.GW.showConstraints), &(SS.GW.ToggleBool)
);
Printf(false, "%Bt%Ft "
"%Fp%Ll%D%fshaded%E "
"%Fp%Ll%D%fedges%E "
"%Fp%Ll%D%fmesh%E "
"%Fp%Ll%D%ffaces%E "
"%Fp%Ll%D%fhidden-lns%E",
hs(SS.GW.showShaded), (DWORD)(&SS.GW.showShaded), &(SS.GW.ToggleBool),
hs(SS.GW.showEdges), (DWORD)(&SS.GW.showEdges), &(SS.GW.ToggleBool),
hs(SS.GW.showMesh), (DWORD)(&SS.GW.showMesh), &(SS.GW.ToggleBool),
hs(SS.GW.showFaces), (DWORD)(&SS.GW.showFaces), &(SS.GW.ToggleBool),
hs(SS.GW.showHdnLines), (DWORD)(&SS.GW.showHdnLines), &(SS.GW.ToggleBool)
);
// Leave space for the icons that are painted here.
Printf(false, "");
Printf(false, "");
}
//-----------------------------------------------------------------------------

View File

@ -1,4 +1,5 @@
#include "solvespace.h"
#include "obj/icons-proto.h"
#include <stdarg.h>
const TextWindow::Color TextWindow::fgColors[] = {
@ -22,6 +23,22 @@ const TextWindow::Color TextWindow::bgColors[] = {
{ 0, 0 },
};
bool TextWindow::SPACER = false;
TextWindow::HideShowIcon TextWindow::hideShowIcons[] = {
{ &(SS.GW.showWorkplanes), Icon_workplane, "workplanes from inactive groups"},
{ &(SS.GW.showNormals), Icon_normal, "normals" },
{ &(SS.GW.showPoints), Icon_point, "points" },
{ &(SS.GW.showConstraints), Icon_constraint, "constraints and dimensions" },
{ &(SS.GW.showFaces), Icon_faces, "XXX - special cased" },
{ &SPACER, 0 },
{ &(SS.GW.showShaded), Icon_shaded, "shaded view of solid model" },
{ &(SS.GW.showEdges), Icon_edges, "edges of solid model" },
{ &(SS.GW.showMesh), Icon_mesh, "triangle mesh of solid model" },
{ &SPACER, 0 },
{ &(SS.GW.showHdnLines), Icon_hidden_lines, "hidden lines" },
{ 0, 0 },
};
void TextWindow::MakeColorTable(const Color *in, float *out) {
int i;
for(i = 0; in[i].c != 0; i++) {
@ -259,7 +276,140 @@ void TextWindow::Show(void) {
InvalidateText();
}
void TextWindow::Paint(int width, int height) {
void TextWindow::TimerCallback(void)
{
tooltippedIcon = hoveredIcon;
InvalidateText();
}
void TextWindow::DrawOrHitTestIcons(int how, double mx, double my)
{
int width, height;
GetTextWindowSize(&width, &height);
int x = 20, y = 33 + LINE_HEIGHT;
y -= scrollPos*(LINE_HEIGHT/2);
double grey = 30.0/255;
double top = y - 28, bot = y + 4;
glColor4d(grey, grey, grey, 1.0);
glxAxisAlignedQuad(0, width, top, bot);
HideShowIcon *oldHovered = hoveredIcon;
if(how != PAINT) {
hoveredIcon = NULL;
}
HideShowIcon *hsi;
for(hsi = &(hideShowIcons[0]); hsi->var; hsi++) {
if(hsi->var == &SPACER) {
// Draw a darker-grey spacer in between the groups of icons.
if(how == PAINT) {
int l = x, r = l + 4,
t = y, b = t - 24;
glColor4d(0.17, 0.17, 0.17, 1);
glxAxisAlignedQuad(l, r, t, b);
}
x += 12;
continue;
}
if(how == PAINT) {
glPushMatrix();
glTranslated(x, y-24, 0);
// Only thing that matters about the color is the alpha,
// should be one for no transparency
glColor3d(0, 0, 0);
glxDrawPixelsWithTexture(hsi->icon, 24, 24);
glPopMatrix();
if(hsi == hoveredIcon) {
glColor4d(1, 1, 0, 0.3);
glxAxisAlignedQuad(x - 2, x + 26, y + 2, y - 26);
}
if(!*(hsi->var)) {
glColor4d(1, 0, 0, 0.6);
glLineWidth(2);
int s = 0, f = 24;
glBegin(GL_LINES);
glVertex2d(x+s, y-s);
glVertex2d(x+f, y-f);
glVertex2d(x+s, y-f);
glVertex2d(x+f, y-s);
glEnd();
}
} 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(hsi != tooltippedIcon) {
oldMousePos = Point2d::From(mx, my);
}
if(hsi != oldHovered || how == CLICK) {
SetTimerFor(1000);
}
hoveredIcon = hsi;
if(how == CLICK) {
SS.GW.ToggleBool(hsi->var);
}
}
}
x += 32;
}
if(how != PAINT && hoveredIcon != oldHovered) {
InvalidateText();
}
if(tooltippedIcon) {
if(how == PAINT) {
char str[1024];
if(tooltippedIcon->icon == Icon_faces) {
if(SS.GW.showFaces) {
strcpy(str, "Don't select faces with mouse");
} else {
strcpy(str, "Select faces with mouse");
}
} else {
sprintf(str, "%s %s", *(tooltippedIcon->var) ? "Hide" : "Show",
tooltippedIcon->tip);
}
double ox = oldMousePos.x, oy = oldMousePos.y - LINE_HEIGHT;
int tw = (strlen(str) + 1)*CHAR_WIDTH;
ox = min(ox, (width - 25) - tw);
oy = max(oy, 5);
glxCreateBitmapFont();
glLineWidth(1);
glColor4d(1.0, 1.0, 0.6, 1.0);
glxAxisAlignedQuad(ox, ox+tw, oy, oy+LINE_HEIGHT);
glColor4d(0.0, 0.0, 0.0, 1.0);
glxAxisAlignedLineLoop(ox, ox+tw, oy, oy+LINE_HEIGHT);
glColor4d(0, 0, 0, 1);
glxBitmapText(str, Vector::From(ox+5, oy-3+LINE_HEIGHT, 0));
} else {
if(!hoveredIcon ||
(hoveredIcon != tooltippedIcon))
{
tooltippedIcon = NULL;
InvalidateGraphics();
}
// And if we're hovered, then we've set a timer that will cause
// us to show the tool tip later.
}
}
}
void TextWindow::Paint(void) {
int width, height;
GetTextWindowSize(&width, &height);
// We would like things pixel-exact, to avoid shimmering.
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
@ -276,12 +426,12 @@ void TextWindow::Paint(int width, int height) {
halfRows = height / (LINE_HEIGHT/2);
int bottom = SS.TW.top[SS.TW.rows-1] + 2;
int bottom = top[rows-1] + 2;
scrollPos = min(scrollPos, bottom - halfRows);
scrollPos = max(scrollPos, 0);
// Let's set up the scroll bar first
MoveTextScrollbarTo(scrollPos, SS.TW.top[SS.TW.rows - 1] + 1, halfRows);
MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows);
// Create the bitmap font that we're going to use.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -289,33 +439,31 @@ void TextWindow::Paint(int width, int height) {
// Now paint the window.
int r, c, a;
for(a = 0; a < 3; a++) {
for(a = 0; a < 2; a++) {
if(a == 0) {
glBegin(GL_QUADS);
} else if(a == 1) {
glEnable(GL_TEXTURE_2D);
glxCreateBitmapFont();
glBegin(GL_QUADS);
} else {
glBegin(GL_LINES);
}
for(r = 0; r < SS.TW.rows; r++) {
int top = SS.TW.top[r];
if(top < (scrollPos-1)) continue;
if(top > scrollPos+halfRows) break;
for(r = 0; r < rows; r++) {
int ltop = top[r];
if(ltop < (scrollPos-1)) continue;
if(ltop > scrollPos+halfRows) break;
for(c = 0; c < min((width/CHAR_WIDTH)+1, SS.TW.MAX_COLS); c++) {
for(c = 0; c < min((width/CHAR_WIDTH)+1, MAX_COLS); c++) {
int x = LEFT_MARGIN + c*CHAR_WIDTH;
int y = (top-scrollPos)*(LINE_HEIGHT/2) + 2;
int y = (ltop-scrollPos)*(LINE_HEIGHT/2) + 4;
int fg = SS.TW.meta[r][c].fg;
int bg = SS.TW.meta[r][c].bg;
int fg = meta[r][c].fg;
int bg = meta[r][c].bg;
// On the first pass, all the background quads; on the next
// pass, all the foreground (i.e., font) quads.
if(a == 0) {
int bh = LINE_HEIGHT, adj = 0;
int bh = LINE_HEIGHT, adj = -2;
if(bg & 0x80000000) {
glColor3f(REDf(bg), GREENf(bg), BLUEf(bg));
bh = CHAR_HEIGHT;
@ -328,23 +476,45 @@ void TextWindow::Paint(int width, int height) {
// Move the quad down a bit, so that the descenders
// still have the correct background.
y += adj;
glBegin(GL_QUADS);
glVertex2d(x, y);
glVertex2d(x + CHAR_WIDTH, y);
glVertex2d(x + CHAR_WIDTH, y + bh);
glVertex2d(x, y + bh);
glEnd();
glxAxisAlignedQuad(x, x + CHAR_WIDTH, y, y + bh);
y -= adj;
}
} else if(a == 1) {
glColor3fv(&(fgColorTable[fg*3]));
glxBitmapCharQuad(SS.TW.text[r][c], x, y + CHAR_HEIGHT);
} else {
if(SS.TW.meta[r][c].link && SS.TW.meta[r][c].link != 'n') {
glColor3fv(&(fgColorTable[fg*3]));
y += CHAR_HEIGHT + 1;
glVertex2d(x, y);
glVertex2d(x + CHAR_WIDTH, y);
glxBitmapCharQuad(text[r][c], x, y + CHAR_HEIGHT);
// If this is a link and it's hovered, then draw the
// underline.
if(meta[r][c].link && meta[r][c].link != 'n' &&
(r == hoveredRow && c == hoveredCol))
{
int cs = c, cf = c;
while(cs >= 0 && meta[r][cs].link &&
meta[r][cs].f == meta[r][c].f &&
meta[r][cs].data == meta[r][c].data)
{
cs--;
}
cs++;
while( meta[r][cf].link &&
meta[r][cf].f == meta[r][c].f &&
meta[r][cf].data == meta[r][c].data)
{
cf++;
}
glEnd();
glDisable(GL_TEXTURE_2D);
glLineWidth(1);
glBegin(GL_LINES);
int yp = y + CHAR_HEIGHT;
glVertex2d(LEFT_MARGIN + cs*CHAR_WIDTH, yp);
glVertex2d(LEFT_MARGIN + cf*CHAR_WIDTH, yp);
glEnd();
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
}
}
}
@ -353,6 +523,9 @@ void TextWindow::Paint(int width, int height) {
glEnd();
glDisable(GL_TEXTURE_2D);
}
// The header has some icons that are drawn separately from the text
DrawOrHitTestIcons(PAINT, 0, 0);
}
void TextWindow::MouseEvent(bool leftClick, double x, double y) {
@ -366,29 +539,39 @@ void TextWindow::MouseEvent(bool leftClick, double x, double y) {
return;
}
DrawOrHitTestIcons(leftClick ? CLICK : HOVER, x, y);
GraphicsWindow::Selection ps = SS.GW.hover;
SS.GW.hover.Clear();
int prevHoveredRow = hoveredRow,
prevHoveredCol = hoveredCol;
hoveredRow = 0;
hoveredCol = 0;
// Find the corresponding character in the text buffer
int c = (int)((x - LEFT_MARGIN) / CHAR_WIDTH);
int hh = (LINE_HEIGHT)/2;
y += scrollPos*hh;
int r;
for(r = 0; r < SS.TW.rows; r++) {
if(y >= SS.TW.top[r]*hh && y <= (SS.TW.top[r]+2)*hh) {
for(r = 0; r < rows; r++) {
if(y >= top[r]*hh && y <= (top[r]+2)*hh) {
break;
}
}
if(r >= SS.TW.rows) {
if(r >= rows) {
SetMousePointerToHand(false);
goto done;
}
#define META (SS.TW.meta[r][c])
hoveredRow = r;
hoveredCol = c;
#define META (meta[r][c])
if(leftClick) {
if(META.link && META.f) {
(META.f)(META.link, META.data);
SS.TW.Show();
Show();
InvalidateGraphics();
}
} else {
@ -403,19 +586,30 @@ void TextWindow::MouseEvent(bool leftClick, double x, double y) {
}
done:
if(!ps.Equals(&(SS.GW.hover))) {
if((!ps.Equals(&(SS.GW.hover))) ||
prevHoveredRow != hoveredRow ||
prevHoveredCol != hoveredCol)
{
InvalidateGraphics();
InvalidateText();
}
}
void TextWindow::MouseLeave(void) {
tooltippedIcon = NULL;
hoveredRow = 0;
hoveredCol = 0;
InvalidateText();
}
void TextWindow::ScrollbarEvent(int newPos) {
int bottom = SS.TW.top[SS.TW.rows-1] + 2;
int bottom = top[rows-1] + 2;
newPos = min(newPos, bottom - halfRows);
newPos = max(newPos, 0);
if(newPos != scrollPos) {
scrollPos = newPos;
MoveTextScrollbarTo(scrollPos, SS.TW.top[SS.TW.rows - 1] + 1, halfRows);
MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows);
if(TextEditControlIsVisible()) {
extern int TextEditControlCol, TextEditControlHalfRow;

View File

@ -39,8 +39,8 @@ static const struct {
{ Icon_assemble, GraphicsWindow::MNU_GROUP_IMPORT, "New group importing / assembling file" },
{ SPACER },
{ Icon_in3d, GraphicsWindow::MNU_FREE_IN_3D, "Sketch / constrain in 3d" },
{ Icon_ontoworkplane, GraphicsWindow::MNU_SEL_WORKPLANE, "Sketch / constrain in workplane" },
{ Icon_in3d, GraphicsWindow::MNU_NEAREST_ISO, "Nearest isometric view" },
{ Icon_ontoworkplane, GraphicsWindow::MNU_ONTO_WORKPLANE, "Align view to active workplane" },
{ NULL },
};
@ -122,12 +122,7 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
double c = 30.0/255;
glColor4d(c, c, c, 1.0);
glBegin(GL_QUADS);
glVertex2d(aleft, atop);
glVertex2d(aleft, abot);
glVertex2d(aright, abot);
glVertex2d(aright, atop);
glEnd();
glxAxisAlignedQuad(aleft, aright, atop, abot);
}
struct {
@ -151,12 +146,7 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
glColor4d(0.17, 0.17, 0.17, 1);
x += 16;
y += 24;
glBegin(GL_QUADS);
glVertex2d(x+divw, y+divh);
glVertex2d(x+divw, y-divh);
glVertex2d(x-divw, y-divh);
glVertex2d(x-divw, y+divh);
glEnd();
glxAxisAlignedQuad(x+divw, x-divw, y+divh, y-divh);
x -= 16;
y -= 24;
}
@ -172,12 +162,7 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
// Highlight the hovered or pending item.
glColor4d(1, 1, 0, 0.3);
int boxhw = 15;
glBegin(GL_QUADS);
glVertex2d(x+boxhw, y+boxhw);
glVertex2d(x+boxhw, y-boxhw);
glVertex2d(x-boxhw, y-boxhw);
glVertex2d(x-boxhw, y+boxhw);
glEnd();
glxAxisAlignedQuad(x+boxhw, x-boxhw, y+boxhw, y-boxhw);
}
if(toolbarTooltipped == Toolbar[i].menu) {
@ -218,13 +203,16 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
if(toolbarTooltipped == SS.GW.menu[i].id) {
int accel = SS.GW.menu[i].accel;
int ac = accel & 0xff;
char *s = str+strlen(str);
if(isalnum(ac) || ac == '[') {
char *s = str+strlen(str);
if(accel & 0x100) {
sprintf(s, " (Shift+%c)", ac);
} else if((accel & ~0xff) == 0) {
sprintf(s, " (%c)", ac);
}
} else if(ac == 0xf3) {
sprintf(s, " (F3)");
}
break;
}
@ -236,19 +224,9 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
double ox = toolbarMouseX + 3, oy = toolbarMouseY + 3;
glLineWidth(1);
glColor4d(1.0, 1.0, 0.6, 1.0);
glBegin(GL_QUADS);
glVertex2d(ox, oy);
glVertex2d(ox+tw, oy);
glVertex2d(ox+tw, oy+th);
glVertex2d(ox, oy+th);
glEnd();
glxAxisAlignedQuad(ox, ox+tw, oy, oy+th);
glColor4d(0.0, 0.0, 0.0, 1.0);
glBegin(GL_LINE_LOOP);
glVertex2d(ox, oy);
glVertex2d(ox+tw, oy);
glVertex2d(ox+tw, oy+th);
glVertex2d(ox, oy+th);
glEnd();
glxAxisAlignedLineLoop(ox, ox+tw, oy, oy+th);
glColor4d(0, 0, 0, 1);
glPushMatrix();

31
ui.h
View File

@ -31,7 +31,7 @@ public:
static const int CHAR_WIDTH = 9;
static const int CHAR_HEIGHT = 16;
static const int LINE_HEIGHT = 20;
static const int LEFT_MARGIN = 4;
static const int LEFT_MARGIN = 6;
int scrollPos; // The scrollbar position, in half-row units
int halfRows; // The height of our window, in half-row units
@ -47,15 +47,35 @@ public:
LinkFunction *f;
LinkFunction *h;
} meta[MAX_ROWS][MAX_COLS];
int top[MAX_ROWS]; // in half-line units, or -1 for unused
int hoveredRow, hoveredCol;
int top[MAX_ROWS]; // in half-line units, or -1 for unused
int rows;
// The row of icons at the top of the text window, to hide/show things
typedef struct {
bool *var;
BYTE *icon;
char *tip;
} HideShowIcon;
static HideShowIcon hideShowIcons[];
static bool SPACER;
// These are called by the platform-specific code.
void Paint(int w, int h);
void Paint(void);
void MouseEvent(bool leftDown, double x, double y);
void MouseScroll(double x, double y, int delta);
void MouseLeave(void);
void ScrollbarEvent(int newPos);
static const int PAINT = 0;
static const int HOVER = 1;
static const int CLICK = 2;
void DrawOrHitTestIcons(int how, double mx, double my);
void TimerCallback(void);
Point2d oldMousePos;
HideShowIcon *hoveredIcon, *tooltippedIcon;
void Init(void);
void MakeColorTable(const Color *in, float *out);
@ -282,6 +302,7 @@ public:
MNU_ZOOM_TO_FIT,
MNU_SHOW_GRID,
MNU_PARALLEL_PROJ,
MNU_ONTO_WORKPLANE,
MNU_NEAREST_ORTHO,
MNU_NEAREST_ISO,
MNU_CENTER_VIEW,
@ -567,7 +588,7 @@ public:
bool showFaces;
bool showMesh;
bool showHdnLines;
static void ToggleBool(int link, DWORD v);
void ToggleBool(bool *v);
bool showSnapGrid;
@ -578,7 +599,7 @@ public:
void UpdateDraggedPoint(hEntity hp, double mx, double my);
// These are called by the platform-specific code.
void Paint(int w, int h);
void Paint(void);
void MouseMoved(double x, double y, bool leftDown, bool middleDown,
bool rightDown, bool shiftDown, bool ctrlDown);
void MouseLeftDown(double x, double y);

View File

@ -229,6 +229,7 @@ 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 SetTimerFor(int milliseconds)
{
@ -246,6 +247,10 @@ void GetGraphicsWindowSize(int *w, int *h)
{
GetWindowSize(GraphicsWnd, w, h);
}
void GetTextWindowSize(int *w, int *h)
{
GetWindowSize(TextWnd, w, h);
}
void OpenWebsite(char *url) {
ShellExecute(GraphicsWnd, "open", url, NULL, NULL, SW_SHOWNORMAL);
@ -291,9 +296,7 @@ static void PaintTextWnd(HDC hdc)
{
wglMakeCurrent(GetDC(TextWnd), TextGl);
int w, h;
GetWindowSize(TextWnd, &w, &h);
SS.TW.Paint(w, h);
SS.TW.Paint();
SwapBuffers(GetDC(TextWnd));
// Leave the graphics window context active, except when we're painting
@ -439,8 +442,21 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
break;
}
case WM_MOUSELEAVE:
SS.TW.MouseLeave();
break;
case WM_LBUTTONDOWN:
case WM_MOUSEMOVE: {
// We need this in order to get the WM_MOUSELEAVE
TRACKMOUSEEVENT tme;
ZERO(&tme);
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = TextWnd;
TrackMouseEvent(&tme);
// And process the actual message
int x = LOWORD(lParam);
int y = HIWORD(lParam);
SS.TW.MouseEvent(msg == WM_LBUTTONDOWN, x, y);
@ -595,9 +611,7 @@ static void CreateGlContext(HWND hwnd, HGLRC *glrc)
void PaintGraphics(void)
{
int w, h;
GetWindowSize(GraphicsWnd, &w, &h);
SS.GW.Paint(w, h);
SS.GW.Paint();
SwapBuffers(GetDC(GraphicsWnd));
}
void InvalidateGraphics(void)
@ -1021,7 +1035,7 @@ static void CreateMainWindows(void)
// We get the desired Alt+Tab behaviour by specifying that the text
// window is a child of the graphics window.
TextWnd = CreateWindowEx(0,
"TextWnd", "SolveSpace (Text Window)", WS_THICKFRAME | WS_CLIPCHILDREN,
"TextWnd", "SolveSpace - Browser", WS_THICKFRAME | WS_CLIPCHILDREN,
650, 500, 420, 300, GraphicsWnd, (HMENU)NULL, Instance, NULL);
if(!TextWnd) oops();

View File

@ -1,9 +1,8 @@
replace show/hide links with icons
add checked/unchecked checkbox and radio button
fix bug with rotation in plane where green line stays displayed
lock point where dragged constraint
remove toolbar icons for sketch in 3d, add View -> Align to Workplane
expose transformed point stuff in library, and email McNeel
assign default name for import groups based on filename
-----
rounding, as a special group