768 lines
21 KiB
C
768 lines
21 KiB
C
//
|
|
// Copyright (c) 2013 Mikko Mononen memon@inside.org
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
//
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <iconv.h>
|
|
#include <math.h>
|
|
#include <GLFW/glfw3.h>
|
|
#include "nanovg.h"
|
|
#define GLNANOVG_IMPLEMENTATION
|
|
#include "glnanovg.h"
|
|
|
|
#define ICON_SEARCH 0x1F50D
|
|
#define ICON_CIRCLED_CROSS 0x2716
|
|
#define ICON_CHEVRON_RIGHT 0xE75E
|
|
#define ICON_CHECK 0x2713
|
|
#define ICON_LOGIN 0xE740
|
|
#define ICON_TRASH 0xE729
|
|
|
|
static char* cpToUTF8(int cp, char* str)
|
|
{
|
|
int n = 0;
|
|
if (cp < 0x80) n = 1;
|
|
else if (cp < 0x800) n = 2;
|
|
else if (cp < 0x10000) n = 3;
|
|
else if (cp < 0x200000) n = 4;
|
|
else if (cp < 0x4000000) n = 5;
|
|
else if (cp <= 0x7fffffff) n = 6;
|
|
str[n] = '\0';
|
|
switch (n) {
|
|
case 6: str[5] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x4000000;
|
|
case 5: str[4] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x200000;
|
|
case 4: str[3] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x10000;
|
|
case 3: str[2] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0x800;
|
|
case 2: str[1] = 0x80 | (cp & 0x3f); cp = cp >> 6; cp |= 0xc0;
|
|
case 1: str[0] = cp;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
void drawWindow(struct NVGcontext* vg, const char* title, float x, float y, float w, float h)
|
|
{
|
|
float cornerRadius = 3.0f;
|
|
struct NVGpaint shadowPaint;
|
|
struct NVGpaint headerPaint;
|
|
|
|
nvgSave(vg);
|
|
// nvgClearState(vg);
|
|
|
|
// Window
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
|
|
nvgFillColor(vg, nvgRGBA(28,30,34,192));
|
|
// nvgFillColor(vg, nvgRGBA(0,0,0,128));
|
|
nvgFill(vg);
|
|
|
|
// Drop shadow
|
|
shadowPaint = nvgBoxGradient(vg, x,y+2, w,h, cornerRadius*2, 10, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
|
|
nvgBeginPath(vg);
|
|
nvgRect(vg, x-10,y-10, w+20,h+30);
|
|
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
|
|
nvgPathWinding(vg, NVG_HOLE);
|
|
nvgFillPaint(vg, shadowPaint);
|
|
nvgFill(vg);
|
|
|
|
// Header
|
|
headerPaint = nvgLinearGradient(vg, x,y,x,y+15, nvgRGBA(255,255,255,8), nvgRGBA(0,0,0,16));
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+1,y+1, w-2,30, cornerRadius-1);
|
|
nvgFillPaint(vg, headerPaint);
|
|
nvgFill(vg);
|
|
nvgBeginPath(vg);
|
|
nvgMoveTo(vg, x+0.5f, y+0.5f+30);
|
|
nvgLineTo(vg, x+0.5f+w-1, y+0.5f+30);
|
|
nvgStrokeColor(vg, nvgRGBA(0,0,0,32));
|
|
nvgStroke(vg);
|
|
|
|
nvgFontSize(vg, 18.0f);
|
|
nvgFontFace(vg, "sans-bold");
|
|
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
|
|
|
|
nvgFontBlur(vg,2);
|
|
nvgFillColor(vg, nvgRGBA(0,0,0,128));
|
|
nvgText(vg, x+w/2,y+16+1, title);
|
|
|
|
nvgFontBlur(vg,0);
|
|
nvgFillColor(vg, nvgRGBA(220,220,220,160));
|
|
nvgText(vg, x+w/2,y+16, title);
|
|
|
|
nvgRestore(vg);
|
|
}
|
|
|
|
void drawSearchBox(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
|
|
{
|
|
struct NVGpaint bg;
|
|
char icon[8];
|
|
float cornerRadius = h/2-1;
|
|
|
|
// Edit
|
|
bg = nvgBoxGradient(vg, x,y+1.5f, w,h, h/2,5, nvgRGBA(0,0,0,16), nvgRGBA(0,0,0,92));
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
/* nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
|
|
nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
|
|
nvgStroke(vg);*/
|
|
|
|
nvgFontSize(vg, h*1.3f);
|
|
nvgFontFace(vg, "icons");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,64));
|
|
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+h*0.55f, y+h*0.55f, cpToUTF8(ICON_SEARCH,icon));
|
|
|
|
nvgFontSize(vg, 20.0f);
|
|
nvgFontFace(vg, "sans");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,32));
|
|
|
|
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+h*1.05f,y+h*0.5f,text);
|
|
|
|
nvgFontSize(vg, h*1.3f);
|
|
nvgFontFace(vg, "icons");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,32));
|
|
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+w-h*0.55f, y+h*0.55f, cpToUTF8(ICON_CIRCLED_CROSS,icon));
|
|
}
|
|
|
|
void drawDropDown(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
|
|
{
|
|
struct NVGpaint bg;
|
|
char icon[8];
|
|
float cornerRadius = 4.0f;
|
|
|
|
bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16));
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
|
|
nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
|
|
nvgStroke(vg);
|
|
|
|
nvgFontSize(vg, 20.0f);
|
|
nvgFontFace(vg, "sans");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,160));
|
|
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+h*0.3f,y+h*0.5f,text);
|
|
|
|
nvgFontSize(vg, h*1.3f);
|
|
nvgFontFace(vg, "icons");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,64));
|
|
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+w-h*0.5f, y+h*0.5f, cpToUTF8(ICON_CHEVRON_RIGHT,icon));
|
|
}
|
|
|
|
void drawLabel(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
|
|
{
|
|
nvgFontSize(vg, 18.0f);
|
|
nvgFontFace(vg, "sans");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,128));
|
|
|
|
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x,y+h*0.5f,text);
|
|
}
|
|
|
|
void drawEditBoxBase(struct NVGcontext* vg, float x, float y, float w, float h)
|
|
{
|
|
struct NVGpaint bg;
|
|
// Edit
|
|
bg = nvgBoxGradient(vg, x+1,y+1+1.5f, w-2,h-2, 3,4, nvgRGBA(255,255,255,32), nvgRGBA(32,32,32,32));
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+1,y+1, w-2,h-2, 4-1);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, 4-0.5f);
|
|
nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
|
|
nvgStroke(vg);
|
|
}
|
|
|
|
void drawEditBox(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
|
|
{
|
|
|
|
drawEditBoxBase(vg, x,y, w,h);
|
|
|
|
nvgFontSize(vg, 20.0f);
|
|
nvgFontFace(vg, "sans");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,64));
|
|
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+h*0.3f,y+h*0.5f,text);
|
|
}
|
|
|
|
void drawEditBoxNum(struct NVGcontext* vg,
|
|
const char* text, const char* units, float x, float y, float w, float h)
|
|
{
|
|
float uw;
|
|
|
|
drawEditBoxBase(vg, x,y, w,h);
|
|
|
|
nvgTextBounds(vg, units, &uw, NULL);
|
|
|
|
nvgFontSize(vg, 18.0f);
|
|
nvgFontFace(vg, "sans");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,64));
|
|
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+w-h*0.3f,y+h*0.5f,units);
|
|
|
|
nvgFontSize(vg, 20.0f);
|
|
nvgFontFace(vg, "sans");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,128));
|
|
nvgTextAlign(vg,NVG_ALIGN_RIGHT|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+w-uw-h*0.5f,y+h*0.5f,text);
|
|
}
|
|
|
|
void drawCheckBox(struct NVGcontext* vg, const char* text, float x, float y, float w, float h)
|
|
{
|
|
struct NVGpaint bg;
|
|
char icon[8];
|
|
|
|
nvgFontSize(vg, 18.0f);
|
|
nvgFontFace(vg, "sans");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,160));
|
|
|
|
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+28,y+h*0.5f,text);
|
|
|
|
bg = nvgBoxGradient(vg, x+1,y+(int)(h*0.5f)-9+1, 18,18, 3,3, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,92));
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+1,y+(int)(h*0.5f)-9, 18,18, 3);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
nvgFontSize(vg, 40);
|
|
nvgFontFace(vg, "icons");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,128));
|
|
nvgTextAlign(vg,NVG_ALIGN_CENTER|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+9+2, y+h*0.5f, cpToUTF8(ICON_CHECK,icon));
|
|
}
|
|
|
|
void drawButton(struct NVGcontext* vg, int preicon, const char* text, float x, float y, float w, float h, unsigned int col)
|
|
{
|
|
struct NVGpaint bg;
|
|
char icon[8];
|
|
float cornerRadius = 4.0f;
|
|
float tw = 0, iw = 0;
|
|
|
|
bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(255,255,255,col==0?16:32), nvgRGBA(0,0,0,col==0?16:32));
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+1,y+1, w-2,h-2, cornerRadius-1);
|
|
if (col != 0) {
|
|
nvgFillColor(vg, col);
|
|
nvgFill(vg);
|
|
}
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+0.5f,y+0.5f, w-1,h-1, cornerRadius-0.5f);
|
|
nvgStrokeColor(vg, nvgRGBA(0,0,0,48));
|
|
nvgStroke(vg);
|
|
|
|
nvgFontSize(vg, 20.0f);
|
|
nvgFontFace(vg, "sans-bold");
|
|
nvgTextBounds(vg, text, &tw, NULL);
|
|
if (preicon != 0) {
|
|
nvgFontSize(vg, h*1.3f);
|
|
nvgFontFace(vg, "icons");
|
|
nvgTextBounds(vg, cpToUTF8(preicon,icon), &iw, NULL);
|
|
iw += h*0.15f;
|
|
}
|
|
|
|
if (preicon != 0) {
|
|
nvgFontSize(vg, h*1.3f);
|
|
nvgFontFace(vg, "icons");
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,96));
|
|
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
|
|
nvgText(vg, x+w*0.5f-tw*0.5f-iw*0.75f, y+h*0.5f, cpToUTF8(preicon,icon));
|
|
}
|
|
|
|
nvgFontSize(vg, 20.0f);
|
|
nvgFontFace(vg, "sans-bold");
|
|
nvgTextAlign(vg,NVG_ALIGN_LEFT|NVG_ALIGN_MIDDLE);
|
|
nvgFillColor(vg, nvgRGBA(0,0,0,160));
|
|
nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f-1,text);
|
|
nvgFillColor(vg, nvgRGBA(255,255,255,160));
|
|
nvgText(vg, x+w*0.5f-tw*0.5f+iw*0.25f,y+h*0.5f,text);
|
|
}
|
|
|
|
void drawSlider(struct NVGcontext* vg, float pos, float x, float y, float w, float h)
|
|
{
|
|
struct NVGpaint bg, knob;
|
|
float cy = y+(int)(h*0.5f);
|
|
float kr = (int)(h*0.25f);
|
|
|
|
nvgSave(vg);
|
|
// nvgClearState(vg);
|
|
|
|
// Slot
|
|
bg = nvgBoxGradient(vg, x,cy-2+1, w,4, 2,2, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,128));
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x,cy-2, w,4, 2);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
// Knob Shadow
|
|
bg = nvgRadialGradient(vg, x+(int)(pos*w),cy+1, kr-3,kr+3, nvgRGBA(0,0,0,64), nvgRGBA(0,0,0,0));
|
|
nvgBeginPath(vg);
|
|
nvgRect(vg, x+(int)(pos*w)-kr-5,cy-kr-5,kr*2+5+5,kr*2+5+5+3);
|
|
nvgCircle(vg, x+(int)(pos*w),cy, kr);
|
|
nvgPathWinding(vg, NVG_HOLE);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
// Knob
|
|
knob = nvgLinearGradient(vg, x,cy-kr,x,cy+kr, nvgRGBA(255,255,255,16), nvgRGBA(0,0,0,16));
|
|
nvgBeginPath(vg);
|
|
nvgCircle(vg, x+(int)(pos*w),cy, kr-1);
|
|
nvgFillColor(vg, nvgRGBA(40,43,48,255));
|
|
nvgFill(vg);
|
|
nvgFillPaint(vg, knob);
|
|
nvgFill(vg);
|
|
|
|
nvgBeginPath(vg);
|
|
nvgCircle(vg, x+(int)(pos*w),cy, kr-0.5f);
|
|
nvgStrokeColor(vg, nvgRGBA(0,0,0,92));
|
|
nvgStroke(vg);
|
|
|
|
nvgRestore(vg);
|
|
}
|
|
|
|
void drawEyes(struct NVGcontext* vg, float x, float y, float w, float h, float mx, float my, float t)
|
|
{
|
|
struct NVGpaint gloss, bg;
|
|
float ex = w *0.23f;
|
|
float ey = h * 0.5f;
|
|
float lx = x + ex;
|
|
float ly = y + ey;
|
|
float rx = x + w - ex;
|
|
float ry = y + ey;
|
|
float dx,dy,d;
|
|
float br = (ex < ey ? ex : ey) * 0.5f;
|
|
float blink = 1 - pow(sinf(t*0.5f),200)*0.8f;
|
|
|
|
bg = nvgLinearGradient(vg, x,y+h*0.5f,x+w*0.1f,y+h, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,16));
|
|
nvgBeginPath(vg);
|
|
nvgEllipse(vg, lx+3.0f,ly+16.0f, ex,ey);
|
|
nvgEllipse(vg, rx+3.0f,ry+16.0f, ex,ey);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
bg = nvgLinearGradient(vg, x,y+h*0.25f,x+w*0.1f,y+h, nvgRGBA(220,220,220,255), nvgRGBA(128,128,128,255));
|
|
nvgBeginPath(vg);
|
|
nvgEllipse(vg, lx,ly, ex,ey);
|
|
nvgEllipse(vg, rx,ry, ex,ey);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
dx = (mx - rx) / (ex * 10);
|
|
dy = (my - ry) / (ey * 10);
|
|
d = sqrtf(dx*dx+dy*dy);
|
|
if (d > 1.0f) {
|
|
dx /= d; dy /= d;
|
|
}
|
|
dx *= ex*0.4f;
|
|
dy *= ey*0.5f;
|
|
nvgBeginPath(vg);
|
|
nvgEllipse(vg, lx+dx,ly+dy+ey*0.25f*(1-blink), br,br*blink);
|
|
nvgFillColor(vg, nvgRGBA(32,32,32,255));
|
|
nvgFill(vg);
|
|
|
|
dx = (mx - rx) / (ex * 10);
|
|
dy = (my - ry) / (ey * 10);
|
|
d = sqrtf(dx*dx+dy*dy);
|
|
if (d > 1.0f) {
|
|
dx /= d; dy /= d;
|
|
}
|
|
dx *= ex*0.4f;
|
|
dy *= ey*0.5f;
|
|
nvgBeginPath(vg);
|
|
nvgEllipse(vg, rx+dx,ry+dy+ey*0.25f*(1-blink), br,br*blink);
|
|
nvgFillColor(vg, nvgRGBA(32,32,32,255));
|
|
nvgFill(vg);
|
|
|
|
gloss = nvgRadialGradient(vg, lx-ex*0.25f,ly-ey*0.5f, ex*0.1f,ex*0.75f, nvgRGBA(255,255,255,128), nvgRGBA(255,255,255,0));
|
|
nvgBeginPath(vg);
|
|
nvgEllipse(vg, lx,ly, ex,ey);
|
|
nvgFillPaint(vg, gloss);
|
|
nvgFill(vg);
|
|
|
|
gloss = nvgRadialGradient(vg, rx-ex*0.25f,ry-ey*0.5f, ex*0.1f,ex*0.75f, nvgRGBA(255,255,255,128), nvgRGBA(255,255,255,0));
|
|
nvgBeginPath(vg);
|
|
nvgEllipse(vg, rx,ry, ex,ey);
|
|
nvgFillPaint(vg, gloss);
|
|
nvgFill(vg);
|
|
}
|
|
|
|
void drawGraph(struct NVGcontext* vg, float x, float y, float w, float h, float t)
|
|
{
|
|
struct NVGpaint bg;
|
|
float samples[6];
|
|
float sx[6], sy[6];
|
|
float dx = w/5.0f;
|
|
int i;
|
|
|
|
samples[0] = (1+sinf(t*1.2345f+cosf(t*0.33457f)*0.44f))*0.5f;
|
|
samples[1] = (1+sinf(t*0.68363f+cosf(t*1.3f)*1.55f))*0.5f;
|
|
samples[2] = (1+sinf(t*1.1642f+cosf(t*0.33457)*1.24f))*0.5f;
|
|
samples[3] = (1+sinf(t*0.56345f+cosf(t*1.63f)*0.14f))*0.5f;
|
|
samples[4] = (1+sinf(t*1.6245f+cosf(t*0.254f)*0.3f))*0.5f;
|
|
samples[5] = (1+sinf(t*0.345f+cosf(t*0.03f)*0.6f))*0.5f;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
sx[i] = x+i*dx;
|
|
sy[i] = y+h*samples[i]*0.8f;
|
|
}
|
|
|
|
// Graph background
|
|
bg = nvgLinearGradient(vg, x,y,x,y+h, nvgRGBA(0,160,192,0), nvgRGBA(0,160,192,64));
|
|
nvgBeginPath(vg);
|
|
nvgMoveTo(vg, sx[0], sy[0]);
|
|
for (i = 1; i < 6; i++)
|
|
nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
|
|
nvgLineTo(vg, x+w, y+h);
|
|
nvgLineTo(vg, x, y+h);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
|
|
// Graph line
|
|
nvgBeginPath(vg);
|
|
nvgMoveTo(vg, sx[0], sy[0]+2);
|
|
for (i = 1; i < 6; i++)
|
|
nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1]+2, sx[i]-dx*0.5f,sy[i]+2, sx[i],sy[i]+2);
|
|
nvgStrokeColor(vg, nvgRGBA(0,0,0,32));
|
|
nvgStrokeWidth(vg, 3.0f);
|
|
nvgStroke(vg);
|
|
|
|
nvgBeginPath(vg);
|
|
nvgMoveTo(vg, sx[0], sy[0]);
|
|
for (i = 1; i < 6; i++)
|
|
nvgBezierTo(vg, sx[i-1]+dx*0.5f,sy[i-1], sx[i]-dx*0.5f,sy[i], sx[i],sy[i]);
|
|
nvgStrokeColor(vg, nvgRGBA(0,160,192,255));
|
|
nvgStrokeWidth(vg, 3.0f);
|
|
nvgStroke(vg);
|
|
|
|
// Graph sample pos
|
|
for (i = 0; i < 6; i++) {
|
|
bg = nvgRadialGradient(vg, sx[i],sy[i]+2, 3.0f,8.0f, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,0));
|
|
nvgBeginPath(vg);
|
|
nvgRect(vg, sx[i]-10, sy[i]-10+2, 20,20);
|
|
nvgFillPaint(vg, bg);
|
|
nvgFill(vg);
|
|
}
|
|
|
|
nvgBeginPath(vg);
|
|
for (i = 0; i < 6; i++)
|
|
nvgCircle(vg, sx[i], sy[i], 4.0f);
|
|
nvgFillColor(vg, nvgRGBA(0,160,192,255));
|
|
nvgFill(vg);
|
|
nvgBeginPath(vg);
|
|
for (i = 0; i < 6; i++)
|
|
nvgCircle(vg, sx[i], sy[i], 2.0f);
|
|
nvgFillColor(vg, nvgRGBA(220,220,220,255));
|
|
nvgFill(vg);
|
|
|
|
nvgStrokeWidth(vg, 1.0f);
|
|
}
|
|
|
|
void drawThumbnails(struct NVGcontext* vg, float x, float y, float w, float h, const int* images, int nimages, float t)
|
|
{
|
|
float cornerRadius = 3.0f;
|
|
struct NVGpaint shadowPaint, imgPaint, fadePaint;
|
|
float ix,iy,iw,ih;
|
|
float thumb = 60.0f;
|
|
float arry = 30.5f;
|
|
int imgw, imgh;
|
|
float stackh = (nimages/2) * (thumb+10) + 10;
|
|
int i;
|
|
float u = (1+cosf(t*0.5f))*0.5f;
|
|
|
|
nvgSave(vg);
|
|
// nvgClearState(vg);
|
|
|
|
// Drop shadow
|
|
shadowPaint = nvgBoxGradient(vg, x,y+4, w,h, cornerRadius*2, 20, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
|
|
nvgBeginPath(vg);
|
|
nvgRect(vg, x-10,y-10, w+20,h+30);
|
|
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
|
|
nvgPathWinding(vg, NVG_HOLE);
|
|
nvgFillPaint(vg, shadowPaint);
|
|
nvgFill(vg);
|
|
|
|
// Window
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x,y, w,h, cornerRadius);
|
|
nvgMoveTo(vg, x-10,y+arry);
|
|
nvgLineTo(vg, x+1,y+arry-11);
|
|
nvgLineTo(vg, x+1,y+arry+11);
|
|
nvgFillColor(vg, nvgRGBA(200,200,200,255));
|
|
nvgFill(vg);
|
|
|
|
nvgSave(vg);
|
|
nvgScissor(vg, x,y,w,h);
|
|
nvgTranslate(vg, 0, -(stackh - h)*u);
|
|
|
|
for (i = 0; i < nimages; i++) {
|
|
float tx, ty;
|
|
tx = x+10;
|
|
ty = y+10;
|
|
tx += (i%2) * (thumb+10);
|
|
ty += (i/2) * (thumb+10);
|
|
nvgImageSize(vg, images[i], &imgw, &imgh);
|
|
if (imgw < imgh) {
|
|
iw = thumb;
|
|
ih = iw * (float)imgh/(float)imgw;
|
|
ix = 0;
|
|
iy = -(ih-thumb)*0.5f;
|
|
} else {
|
|
ih = thumb;
|
|
iw = ih * (float)imgw/(float)imgh;
|
|
ix = -(iw-thumb)*0.5f;
|
|
iy = 0;
|
|
}
|
|
imgPaint = nvgImagePattern(vg, tx+ix, ty+iy, iw,ih, 0.0f/180.0f*NVG_PI, images[i], 0);
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, tx,ty, thumb,thumb, 5);
|
|
nvgFillPaint(vg, imgPaint);
|
|
nvgFill(vg);
|
|
|
|
shadowPaint = nvgBoxGradient(vg, tx-1,ty, thumb+2,thumb+2, 5, 3, nvgRGBA(0,0,0,128), nvgRGBA(0,0,0,0));
|
|
nvgBeginPath(vg);
|
|
nvgRect(vg, tx-5,ty-5, thumb+10,thumb+10);
|
|
nvgRoundedRect(vg, tx,ty, thumb,thumb, 6);
|
|
nvgPathWinding(vg, NVG_HOLE);
|
|
nvgFillPaint(vg, shadowPaint);
|
|
nvgFill(vg);
|
|
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, tx+0.5f,ty+0.5f, thumb-1,thumb-1, 4-0.5f);
|
|
nvgStrokeWidth(vg,1.0f);
|
|
nvgStrokeColor(vg, nvgRGBA(255,255,255,192));
|
|
nvgStroke(vg);
|
|
}
|
|
nvgRestore(vg);
|
|
|
|
// Hide fades
|
|
fadePaint = nvgLinearGradient(vg, x,y,x,y+6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0));
|
|
nvgBeginPath(vg);
|
|
nvgRect(vg, x+4,y,w-8,6);
|
|
nvgFillPaint(vg, fadePaint);
|
|
nvgFill(vg);
|
|
|
|
fadePaint = nvgLinearGradient(vg, x,y+h,x,y+h-6, nvgRGBA(200,200,200,255), nvgRGBA(200,200,200,0));
|
|
nvgBeginPath(vg);
|
|
nvgRect(vg, x+4,y+h-6,w-8,6);
|
|
nvgFillPaint(vg, fadePaint);
|
|
nvgFill(vg);
|
|
|
|
// Scroll bar
|
|
shadowPaint = nvgBoxGradient(vg, x+w-12+1,y+4+1, 8,h-8, 3,4, nvgRGBA(0,0,0,32), nvgRGBA(0,0,0,92));
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+w-12,y+4, 8,h-8, 3);
|
|
nvgFillPaint(vg, shadowPaint);
|
|
// nvgFillColor(vg, nvgRGBA(255,0,0,128));
|
|
nvgFill(vg);
|
|
|
|
float scrollh = (h/stackh) * (h-8);
|
|
shadowPaint = nvgBoxGradient(vg, x+w-12-1,y+4+(h-8-scrollh)*u-1, 8,scrollh, 3,4, nvgRGBA(220,220,220,255), nvgRGBA(128,128,128,255));
|
|
nvgBeginPath(vg);
|
|
nvgRoundedRect(vg, x+w-12+1,y+4+1 + (h-8-scrollh)*u, 8-2,scrollh-2, 2);
|
|
nvgFillPaint(vg, shadowPaint);
|
|
// nvgFillColor(vg, nvgRGBA(0,0,0,128));
|
|
nvgFill(vg);
|
|
|
|
nvgRestore(vg);
|
|
}
|
|
|
|
void errorcb(int error, const char* desc)
|
|
{
|
|
printf("GLFW error: %s\n", desc);
|
|
}
|
|
|
|
int blowup = 0;
|
|
|
|
static void key(GLFWwindow* window, int key, int scancode, int action, int mods)
|
|
{
|
|
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
|
|
glfwSetWindowShouldClose(window, GL_TRUE);
|
|
if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
|
|
blowup = !blowup;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
GLFWwindow* window;
|
|
int fontNormal = -1, fontBold = -1, fontIcons = -1;
|
|
struct NVGcontext* vg = NULL;
|
|
int images[12];
|
|
int i;
|
|
|
|
if (!glfwInit()) {
|
|
printf("Failed to init GLFW.");
|
|
return -1;
|
|
}
|
|
|
|
glfwSetErrorCallback(errorcb);
|
|
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
|
|
window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL);
|
|
if (!window) {
|
|
glfwTerminate();
|
|
return -1;
|
|
}
|
|
|
|
glfwSetKeyCallback(window, key);
|
|
|
|
glfwMakeContextCurrent(window);
|
|
|
|
vg = glnvgCreate();
|
|
if (vg == NULL) {
|
|
printf("Could not init nanovg.\n");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < 12; i++) {
|
|
char file[128];
|
|
snprintf(file, 128, "../example/images/image%d.jpg", i+1);
|
|
images[i] = nvgCreateImage(vg, file);
|
|
if (images[i] == 0) {
|
|
printf("Could not load %s.\n", file);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
fontIcons = nvgCreateFont(vg, "icons", "../example/entypo.ttf");
|
|
if (fontIcons == -1) {
|
|
printf("Could not add font icons.\n");
|
|
return -1;
|
|
}
|
|
fontNormal = nvgCreateFont(vg, "sans", "../example/Roboto-Regular.ttf");
|
|
// fontNormal = nvgAddFont(vg, "sans", "../example/FiraSans-Regular.ttf");
|
|
if (fontNormal == -1) {
|
|
printf("Could not add font italic.\n");
|
|
return -1;
|
|
}
|
|
fontBold = nvgCreateFont(vg, "sans-bold", "../example/Roboto-Bold.ttf");
|
|
// fontBold = nvgAddFont(vg, "sans-bold", "../example/FiraSans-Bold.ttf");
|
|
if (fontBold == -1) {
|
|
printf("Could not add font bold.\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
glfwSetTime(0);
|
|
|
|
while (!glfwWindowShouldClose(window))
|
|
{
|
|
// float sx, sy, dx, dy, lh = 0;
|
|
double mx, my;
|
|
int width, height;
|
|
glfwGetCursorPos(window, &mx, &my);
|
|
glfwGetFramebufferSize(window, &width, &height);
|
|
// Update and render
|
|
glViewport(0, 0, width, height);
|
|
glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable(GL_CULL_FACE);
|
|
glDisable(GL_TEXTURE_2D);
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0,width,height,0,-1,1);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
glDisable(GL_DEPTH_TEST);
|
|
glColor4ub(255,255,255,255);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
float t = glfwGetTime();
|
|
float x,y,popy;
|
|
|
|
nvgBeginFrame(vg);
|
|
|
|
drawEyes(vg, width - 250, 50, 200, 120, mx, my, t);
|
|
drawGraph(vg, 0, height/2, width, height/2, t);
|
|
|
|
nvgSave(vg);
|
|
if (blowup) {
|
|
// nvgRotate(vg, 3.0f/180.0f*NVG_PI);
|
|
nvgScale(vg, 2.0f, 2.0f);
|
|
}
|
|
|
|
// Widgets
|
|
drawWindow(vg, "Widgets `n Stuff", 20, 20, 300, 400);
|
|
x = 30; y = 65;
|
|
drawSearchBox(vg, "Search", x,y,280,25);
|
|
y += 40;
|
|
drawDropDown(vg, "Effects", x,y,280,28);
|
|
popy = y + 14;
|
|
y += 45;
|
|
|
|
// Form
|
|
drawLabel(vg, "Login", x,y, 280,20);
|
|
y += 25;
|
|
drawEditBox(vg, "Email", x,y, 280,28);
|
|
y += 35;
|
|
drawEditBox(vg, "Password", x,y, 280,28);
|
|
y += 38;
|
|
drawCheckBox(vg, "Remember me", x,y, 140,28);
|
|
drawButton(vg, ICON_LOGIN, "Sign in", x+138, y, 140, 28, nvgRGBA(0,96,128,255));
|
|
y += 45;
|
|
|
|
// Slider
|
|
drawLabel(vg, "Diameter", x,y, 280,20);
|
|
y += 25;
|
|
drawEditBoxNum(vg, "123.00", "px", x+180,y, 100,28);
|
|
drawSlider(vg, 0.4f, x,y, 170,28);
|
|
y += 55;
|
|
|
|
drawButton(vg, ICON_TRASH, "Delete", x, y, 160, 28, nvgRGBA(128,16,8,255));
|
|
drawButton(vg, 0, "Cancel", x+170, y, 110, 28, nvgRGBA(0,0,0,0));
|
|
|
|
// Thumbnails box
|
|
drawThumbnails(vg, 325, popy-30, 160, 300, images, 12, t);
|
|
|
|
nvgRestore(vg);
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
glfwSwapBuffers(window);
|
|
glfwPollEvents();
|
|
}
|
|
|
|
for (i = 0; i < 12; i++)
|
|
nvgDeleteImage(vg, images[i]);
|
|
|
|
glnvgDelete(vg);
|
|
|
|
glfwTerminate();
|
|
return 0;
|
|
}
|