diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 9e2758cc..ecf689bc 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -3,7 +3,7 @@ namespace Emulator { static const char Name[] = "higan"; - static const char Version[] = "093.02"; + static const char Version[] = "093.03"; static const char Author[] = "byuu"; static const char License[] = "GPLv3"; static const char Website[] = "http://byuu.org/"; diff --git a/nall/Makefile b/nall/Makefile index 50bbca51..bee3d22a 100644 --- a/nall/Makefile +++ b/nall/Makefile @@ -41,7 +41,7 @@ ifeq ($(compiler),) flags := link := else ifeq ($(platform),macosx) - compiler := clang + compiler := clang++ flags := -w -stdlib=libc++ link := -lc++ -lobjc else ifeq ($(platform),bsd) diff --git a/nall/dl.hpp b/nall/dl.hpp index d2a5550e..91c7c073 100644 --- a/nall/dl.hpp +++ b/nall/dl.hpp @@ -8,7 +8,7 @@ #include #include -#if defined(PLATFORM_X) || defined(PLATFORM_OSX) +#if defined(PLATFORM_X) || defined(PLATFORM_MACOSX) #include #elif defined(PLATFORM_WINDOWS) #include @@ -59,7 +59,7 @@ inline void library::close() { dlclose((void*)handle); handle = 0; } -#elif defined(PLATFORM_OSX) +#elif defined(PLATFORM_MACOSX) inline bool library::open(const string& name, const string& path) { if(handle) close(); handle = (uintptr_t)dlopen(string(path, !path.empty() && !path.endswith("/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY); diff --git a/nall/image.hpp b/nall/image.hpp index cf671a87..f8221327 100644 --- a/nall/image.hpp +++ b/nall/image.hpp @@ -11,13 +11,14 @@ namespace nall { struct image { - uint8_t* data = nullptr; - unsigned width = 0; + uint8_t* data = nullptr; + unsigned width = 0; unsigned height = 0; - unsigned pitch = 0; + unsigned pitch = 0; + unsigned size = 0; - bool endian = 0; - unsigned depth = 32; + bool endian = 0; //0 = lsb, 1 = msb + unsigned depth = 32; unsigned stride = 4; struct Channel { @@ -34,10 +35,18 @@ struct image { } }; - Channel alpha = {255u << 24, 8u, 24}; - Channel red = {255u << 16, 8u, 16}; - Channel green = {255u << 8, 8u, 8}; - Channel blue = {255u << 0, 8u, 0}; + enum class blend : unsigned { + add, + sourceAlpha, //color = sourceColor * sourceAlpha + targetColor * (1 - sourceAlpha) + sourceColor, //color = sourceColor + targetAlpha, //color = targetColor * targetAlpha + sourceColor * (1 - targetAlpha) + targetColor, //color = targetColor + }; + + Channel alpha = {255u << 24, 8u, 24u}; + Channel red = {255u << 16, 8u, 16u}; + Channel green = {255u << 8, 8u, 8u}; + Channel blue = {255u << 0, 8u, 0u}; typedef double (*interpolation)(double, double, double, double, double); static inline unsigned bitDepth(uint64_t color); @@ -63,18 +72,27 @@ struct image { inline void free(); inline bool empty() const; inline void allocate(unsigned width, unsigned height); - inline void clear(uint64_t color); + inline bool crop(unsigned x, unsigned y, unsigned width, unsigned height); + inline void impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned x, unsigned y, unsigned width, unsigned height); + inline void fill(uint64_t color = 0); + inline void gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d); + inline void horizontalGradient(uint64_t a, uint64_t b); + inline void verticalGradient(uint64_t a, uint64_t b); inline bool load(const string& filename); //inline bool loadBMP(const uint8_t* data, unsigned size); inline bool loadPNG(const uint8_t* data, unsigned size); - inline void scale(unsigned width, unsigned height, interpolation op); + inline void scale(unsigned width, unsigned height, bool linear = true); inline void transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask); inline void alphaBlend(uint64_t alphaColor); protected: - inline uint64_t interpolate(double mu, const uint64_t* s, interpolation op); - inline void scaleX(unsigned width, interpolation op); - inline void scaleY(unsigned height, interpolation op); + inline uint8_t* allocate(unsigned width, unsigned height, unsigned stride); + alwaysinline uint64_t interpolate1D(int64_t a, int64_t b, uint32_t x); + alwaysinline uint64_t interpolate2D(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y); + inline void scaleLinearWidth(unsigned width); + inline void scaleLinearHeight(unsigned height); + inline void scaleLinear(unsigned width, unsigned height); + inline void scaleNearest(unsigned width, unsigned height); inline bool loadBMP(const string& filename); inline bool loadPNG(const string& filename); }; @@ -131,6 +149,7 @@ image& image::operator=(const image& source) { width = source.width; height = source.height; pitch = source.pitch; + size = source.size; endian = source.endian; stride = source.stride; @@ -140,8 +159,8 @@ image& image::operator=(const image& source) { green = source.green; blue = source.blue; - data = new uint8_t[width * height * stride]; - memcpy(data, source.data, width * height * stride); + data = allocate(width, height, stride); + memcpy(data, source.data, source.size); return *this; } @@ -151,6 +170,7 @@ image& image::operator=(image&& source) { width = source.width; height = source.height; pitch = source.pitch; + size = source.size; endian = source.endian; stride = source.stride; @@ -175,13 +195,13 @@ image::image(image&& source) { image::image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) { this->endian = endian; - this->depth = depth; + this->depth = depth; this->stride = (depth / 8) + ((depth & 7) > 0); alpha = {alphaMask, bitDepth(alphaMask), bitShift(alphaMask)}; - red = {redMask, bitDepth(redMask), bitShift(redMask)}; + red = {redMask, bitDepth(redMask), bitShift(redMask )}; green = {greenMask, bitDepth(greenMask), bitShift(greenMask)}; - blue = {blueMask, bitDepth(blueMask), bitShift(blueMask)}; + blue = {blueMask, bitDepth(blueMask), bitShift(blueMask )}; } image::image(const string& filename) { @@ -231,39 +251,175 @@ bool image::empty() const { void image::allocate(unsigned width, unsigned height) { if(data != nullptr && this->width == width && this->height == height) return; free(); - data = new uint8_t[width * height * stride](); + data = allocate(width, height, stride); pitch = width * stride; + size = height * pitch; this->width = width; this->height = height; } -void image::clear(uint64_t color) { - uint8_t *dp = data; +void image::fill(uint64_t color) { + uint8_t* dp = data; for(unsigned n = 0; n < width * height; n++) { write(dp, color); dp += stride; } } +void image::gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d) { + //create gradient by scaling 2x2 image using linear interpolation + //replace data with gradient data to prevent extra copy + delete[] data; + nall::image gradient; + gradient.endian = endian, gradient.depth = depth, gradient.stride = stride; + gradient.alpha = alpha, gradient.red = red, gradient.green = green, gradient.blue = blue; + gradient.allocate(2, 2); + uint8_t* dp = gradient.data; + gradient.write(dp, a); dp += stride; + gradient.write(dp, b); dp += stride; + gradient.write(dp, c); dp += stride; + gradient.write(dp, d); dp += stride; + gradient.scale(width, height); + data = gradient.data; + gradient.data = nullptr; +} + +void image::horizontalGradient(uint64_t a, uint64_t b) { + gradient(a, b, a, b); +} + +void image::verticalGradient(uint64_t a, uint64_t b) { + gradient(a, a, b, b); +} + bool image::load(const string& filename) { if(loadBMP(filename) == true) return true; if(loadPNG(filename) == true) return true; return false; } -void image::scale(unsigned outputWidth, unsigned outputHeight, interpolation op) { - if(width != outputWidth) scaleX(outputWidth, op); - if(height != outputHeight) scaleY(outputHeight, op); +bool image::crop(unsigned outputX, unsigned outputY, unsigned outputWidth, unsigned outputHeight) { + if(outputX + outputWidth > width) return false; + if(outputY + outputHeight > height) return false; + + uint8_t* outputData = allocate(outputWidth, outputHeight, stride); + unsigned outputPitch = outputWidth * stride; + + #pragma omp parallel for + for(unsigned y = 0; y < outputHeight; y++) { + const uint8_t* sp = data + pitch * (outputY + y) + stride * outputX; + uint8_t* dp = outputData + outputPitch * y; + for(unsigned x = 0; x < outputWidth; x++) { + write(dp, read(sp)); + sp += stride; + dp += stride; + } + } + + delete[] data; + data = outputData; + width = outputWidth; + height = outputHeight; + pitch = outputPitch; + size = width * pitch; + return true; +} + +void image::impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned sourceX, unsigned sourceY, unsigned sourceWidth, unsigned sourceHeight) { + source.transform(endian, depth, alpha.mask, red.mask, green.mask, blue.mask); + + for(unsigned y = 0; y < sourceHeight; y++) { + const uint8_t* sp = source.data + source.pitch * (sourceY + y) + source.stride * sourceX; + uint8_t* dp = data + pitch * (targetY + y) + stride * targetX; + for(unsigned x = 0; x < sourceWidth; x++) { + uint64_t sourceColor = source.read(sp); + uint64_t targetColor = read(dp); + + int64_t sa = (sourceColor & alpha.mask) >> alpha.shift; + int64_t sr = (sourceColor & red.mask ) >> red.shift; + int64_t sg = (sourceColor & green.mask) >> green.shift; + int64_t sb = (sourceColor & blue.mask ) >> blue.shift; + + int64_t da = (targetColor & alpha.mask) >> alpha.shift; + int64_t dr = (targetColor & red.mask ) >> red.shift; + int64_t dg = (targetColor & green.mask) >> green.shift; + int64_t db = (targetColor & blue.mask ) >> blue.shift; + + uint64_t a, r, g, b; + + switch(mode) { + case blend::add: + a = max(sa, da); + r = min(red.mask >> red.shift, ((sr * sa) >> alpha.depth) + ((dr * da) >> alpha.depth)); + g = min(green.mask >> green.shift, ((sg * sa) >> alpha.depth) + ((dg * da) >> alpha.depth)); + b = min(blue.mask >> blue.shift, ((sb * sa) >> alpha.depth) + ((db * da) >> alpha.depth)); + break; + + case blend::sourceAlpha: + a = max(sa, da); + r = dr + (((sr - dr) * sa) >> alpha.depth); + g = dg + (((sg - dg) * sa) >> alpha.depth); + b = db + (((sb - db) * sa) >> alpha.depth); + break; + + case blend::sourceColor: + a = sa; + r = sr; + g = sg; + b = sb; + break; + + case blend::targetAlpha: + a = max(sa, da); + r = sr + (((dr - sr) * da) >> alpha.depth); + g = sg + (((dg - sg) * da) >> alpha.depth); + b = sb + (((db - sb) * da) >> alpha.depth); + break; + + case blend::targetColor: + a = da; + r = dr; + g = dg; + b = db; + break; + } + + write(dp, (a << alpha.shift) | (r << red.shift) | (g << green.shift) | (b << blue.shift)); + sp += source.stride; + dp += stride; + } + } +} + +void image::scale(unsigned outputWidth, unsigned outputHeight, bool linear) { + if(width == outputWidth && height == outputHeight) return; //no scaling necessary + if(linear == false) return scaleNearest(outputWidth, outputHeight); + + if(width == outputWidth ) return scaleLinearHeight(outputHeight); + if(height == outputHeight) return scaleLinearWidth(outputWidth); + + //find fastest scaling method, based on number of interpolation operations required + //magnification usually benefits from two-pass linear interpolation + //minification usually benefits from one-pass bilinear interpolation + unsigned d1wh = ((width * outputWidth ) + (outputWidth * outputHeight)) * 1; + unsigned d1hw = ((height * outputHeight) + (outputWidth * outputHeight)) * 1; + unsigned d2wh = (outputWidth * outputHeight) * 3; + + if(d1wh <= d1hw && d1wh <= d2wh) return scaleLinearWidth(outputWidth), scaleLinearHeight(outputHeight); + if(d1hw <= d2wh) return scaleLinearHeight(outputHeight), scaleLinearWidth(outputWidth); + return scaleLinear(outputWidth, outputHeight); } void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAlphaMask, uint64_t outputRedMask, uint64_t outputGreenMask, uint64_t outputBlueMask) { + if(endian == outputEndian && depth == outputDepth && alpha.mask == outputAlphaMask && red.mask == outputRedMask && green.mask == outputGreenMask && blue.mask == outputBlueMask) return; + image output(outputEndian, outputDepth, outputAlphaMask, outputRedMask, outputGreenMask, outputBlueMask); output.allocate(width, height); #pragma omp parallel for for(unsigned y = 0; y < height; y++) { + const uint8_t* sp = data + pitch * y; uint8_t* dp = output.data + output.pitch * y; - uint8_t* sp = data + pitch * y; for(unsigned x = 0; x < width; x++) { uint64_t color = read(sp); sp += stride; @@ -287,9 +443,9 @@ void image::transform(bool outputEndian, unsigned outputDepth, uint64_t outputAl } void image::alphaBlend(uint64_t alphaColor) { - uint64_t alphaR = (alphaColor & red.mask) >> red.shift; + uint64_t alphaR = (alphaColor & red.mask ) >> red.shift; uint64_t alphaG = (alphaColor & green.mask) >> green.shift; - uint64_t alphaB = (alphaColor & blue.mask) >> blue.shift; + uint64_t alphaB = (alphaColor & blue.mask ) >> blue.shift; #pragma omp parallel for for(unsigned y = 0; y < height; y++) { @@ -298,9 +454,9 @@ void image::alphaBlend(uint64_t alphaColor) { uint64_t color = read(dp); uint64_t colorA = (color & alpha.mask) >> alpha.shift; - uint64_t colorR = (color & red.mask) >> red.shift; + uint64_t colorR = (color & red.mask ) >> red.shift; uint64_t colorG = (color & green.mask) >> green.shift; - uint64_t colorB = (color & blue.mask) >> blue.shift; + uint64_t colorB = (color & blue.mask ) >> blue.shift; double alphaScale = (double)colorA / (double)((1 << alpha.depth) - 1); colorA = (1 << alpha.depth) - 1; @@ -316,99 +472,199 @@ void image::alphaBlend(uint64_t alphaColor) { //protected -uint64_t image::interpolate(double mu, const uint64_t* s, double (*op)(double, double, double, double, double)) { - uint64_t aa = (s[0] & alpha.mask) >> alpha.shift, ar = (s[0] & red.mask) >> red.shift, - ag = (s[0] & green.mask) >> green.shift, ab = (s[0] & blue.mask) >> blue.shift; - uint64_t ba = (s[1] & alpha.mask) >> alpha.shift, br = (s[1] & red.mask) >> red.shift, - bg = (s[1] & green.mask) >> green.shift, bb = (s[1] & blue.mask) >> blue.shift; - uint64_t ca = (s[2] & alpha.mask) >> alpha.shift, cr = (s[2] & red.mask) >> red.shift, - cg = (s[2] & green.mask) >> green.shift, cb = (s[2] & blue.mask) >> blue.shift; - uint64_t da = (s[3] & alpha.mask) >> alpha.shift, dr = (s[3] & red.mask) >> red.shift, - dg = (s[3] & green.mask) >> green.shift, db = (s[3] & blue.mask) >> blue.shift; - - int64_t A = op(mu, aa, ba, ca, da); - int64_t R = op(mu, ar, br, cr, dr); - int64_t G = op(mu, ag, bg, cg, dg); - int64_t B = op(mu, ab, bb, cb, db); - - A = max(0, min(A, (1 << alpha.depth) - 1)); - R = max(0, min(R, (1 << red.depth) - 1)); - G = max(0, min(G, (1 << green.depth) - 1)); - B = max(0, min(B, (1 << blue.depth) - 1)); - - return (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift); +uint8_t* image::allocate(unsigned width, unsigned height, unsigned stride) { + //allocate 1x1 larger than requested; so that linear interpolation does not require bounds-checking + unsigned size = width * height * stride; + unsigned padding = width * stride + stride; + uint8_t* data = new uint8_t[size + padding]; + memset(data + size, 0x00, padding); + return data; } -void image::scaleX(unsigned outputWidth, interpolation op) { - uint8_t* outputData = new uint8_t[outputWidth * height * stride]; +//fixed-point reduction of: a * (1 - x) + b * x +uint64_t image::interpolate1D(int64_t a, int64_t b, uint32_t x) { + return a + (((b - a) * x) >> 32); //a + (b - a) * x +} + +//fixed-point reduction of: a * (1 - x) * (1 - y) + b * x * (1 - y) + c * (1 - x) * y + d * x * y +uint64_t image::interpolate2D(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y) { + a = a + (((b - a) * x) >> 32); //a + (b - a) * x + c = c + (((d - c) * x) >> 32); //c + (d - c) * x + return a + (((c - a) * y) >> 32); //a + (c - a) * y +} + +void image::scaleLinearWidth(unsigned outputWidth) { + uint8_t* outputData = allocate(outputWidth, height, stride); unsigned outputPitch = outputWidth * stride; - double step = (double)width / (double)outputWidth; - const uint8_t* terminal = data + pitch * height; + uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1); #pragma omp parallel for for(unsigned y = 0; y < height; y++) { + uint64_t xfraction = 0; + + const uint8_t* sp = data + pitch * y; uint8_t* dp = outputData + outputPitch * y; - uint8_t* sp = data + pitch * y; - double fraction = 0.0; - uint64_t s[4] = {sp < terminal ? read(sp) : 0}; //B,C (0,1) = center of kernel { 0, 0, 1, 2 } - s[1] = s[0]; - s[2] = sp + stride < terminal ? read(sp += stride) : s[1]; - s[3] = sp + stride < terminal ? read(sp += stride) : s[2]; + uint64_t a = read(sp); + uint64_t b = read(sp + stride); + sp += stride; - for(unsigned x = 0; x < width; x++) { - while(fraction <= 1.0) { - if(dp >= outputData + outputPitch * height) break; - write(dp, interpolate(fraction, (const uint64_t*)&s, op)); + unsigned x = 0; + while(x < outputWidth) { + while(xfraction < 0x100000000 && x++ < outputWidth) { + uint64_t A = interpolate1D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, xfraction); + uint64_t R = interpolate1D((a & red.mask ) >> red.shift , (b & red.mask ) >> red.shift, xfraction); + uint64_t G = interpolate1D((a & green.mask) >> green.shift, (b & green.mask) >> green.shift, xfraction); + uint64_t B = interpolate1D((a & blue.mask ) >> blue.shift , (b & blue.mask ) >> blue.shift, xfraction); + + write(dp, (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift)); dp += stride; - fraction += step; + xfraction += xstride; } - s[0] = s[1]; s[1] = s[2]; s[2] = s[3]; - if(sp + stride < terminal) s[3] = read(sp += stride); - fraction -= 1.0; + sp += stride; + a = b; + b = read(sp); + xfraction -= 0x100000000; } } free(); data = outputData; width = outputWidth; - pitch = width * stride; + pitch = outputPitch; + size = height * pitch; } -void image::scaleY(unsigned outputHeight, interpolation op) { - uint8_t* outputData = new uint8_t[width * outputHeight * stride]; - double step = (double)height / (double)outputHeight; - const uint8_t* terminal = data + pitch * height; +void image::scaleLinearHeight(unsigned outputHeight) { + uint8_t* outputData = allocate(width, outputHeight, stride); + uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1); #pragma omp parallel for for(unsigned x = 0; x < width; x++) { + uint64_t yfraction = 0; + + const uint8_t* sp = data + stride * x; uint8_t* dp = outputData + stride * x; - uint8_t* sp = data + stride * x; - double fraction = 0.0; - uint64_t s[4] = {sp < terminal ? read(sp) : 0}; - s[1] = s[0]; - s[2] = sp + pitch < terminal ? read(sp += pitch) : s[1]; - s[3] = sp + pitch < terminal ? read(sp += pitch) : s[2]; + uint64_t a = read(sp); + uint64_t b = read(sp + pitch); + sp += pitch; - for(unsigned y = 0; y < height; y++) { - while(fraction <= 1.0) { - if(dp >= outputData + pitch * outputHeight) break; - write(dp, interpolate(fraction, (const uint64_t*)&s, op)); + unsigned y = 0; + while(y < outputHeight) { + while(yfraction < 0x100000000 && y++ < outputHeight) { + uint64_t A = interpolate1D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, yfraction); + uint64_t R = interpolate1D((a & red.mask ) >> red.shift, (b & red.mask ) >> red.shift, yfraction); + uint64_t G = interpolate1D((a & green.mask) >> green.shift, (b & green.mask) >> green.shift, yfraction); + uint64_t B = interpolate1D((a & blue.mask ) >> blue.shift, (b & blue.mask ) >> blue.shift, yfraction); + + write(dp, (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift)); dp += pitch; - fraction += step; + yfraction += ystride; } - s[0] = s[1]; s[1] = s[2]; s[2] = s[3]; - if(sp + pitch < terminal) s[3] = read(sp += pitch); - fraction -= 1.0; + sp += pitch; + a = b; + b = read(sp); + yfraction -= 0x100000000; } } free(); data = outputData; height = outputHeight; + size = height * pitch; +} + +void image::scaleLinear(unsigned outputWidth, unsigned outputHeight) { + uint8_t* outputData = allocate(outputWidth, outputHeight, stride); + unsigned outputPitch = outputWidth * stride; + + uint64_t xstride = ((uint64_t)(width - 1) << 32) / max(1u, outputWidth - 1); + uint64_t ystride = ((uint64_t)(height - 1) << 32) / max(1u, outputHeight - 1); + + #pragma omp parallel for + for(unsigned y = 0; y < outputHeight; y++) { + uint64_t yfraction = ystride * y; + uint64_t xfraction = 0; + + const uint8_t* sp = data + pitch * (yfraction >> 32); + uint8_t* dp = outputData + outputPitch * y; + + uint64_t a = read(sp); + uint64_t b = read(sp + stride); + uint64_t c = read(sp + pitch); + uint64_t d = read(sp + pitch + stride); + sp += stride; + + unsigned x = 0; + while(x < outputWidth) { + while(xfraction < 0x100000000 && x++ < outputWidth) { + uint64_t A = interpolate2D((a & alpha.mask) >> alpha.shift, (b & alpha.mask) >> alpha.shift, (c & alpha.mask) >> alpha.shift, (d & alpha.mask) >> alpha.shift, xfraction, yfraction); + uint64_t R = interpolate2D((a & red.mask ) >> red.shift, (b & red.mask ) >> red.shift, (c & red.mask ) >> red.shift, (d & red.mask ) >> red.shift, xfraction, yfraction); + uint64_t G = interpolate2D((a & green.mask) >> green.shift, (b & green.mask) >> green.shift, (c & green.mask) >> green.shift, (d & green.mask) >> green.shift, xfraction, yfraction); + uint64_t B = interpolate2D((a & blue.mask ) >> blue.shift, (b & blue.mask ) >> blue.shift, (c & blue.mask ) >> blue.shift, (d & blue.mask ) >> blue.shift, xfraction, yfraction); + + write(dp, (A << alpha.shift) | (R << red.shift) | (G << green.shift) | (B << blue.shift)); + dp += stride; + xfraction += xstride; + } + + sp += stride; + a = b; + c = d; + b = read(sp); + d = read(sp + pitch); + xfraction -= 0x100000000; + } + } + + free(); + data = outputData; + width = outputWidth; + height = outputHeight; + pitch = outputPitch; + size = height * pitch; +} + +void image::scaleNearest(unsigned outputWidth, unsigned outputHeight) { + uint8_t* outputData = allocate(outputWidth, outputHeight, stride); + unsigned outputPitch = outputWidth * stride; + + uint64_t xstride = ((uint64_t)width << 32) / outputWidth; + uint64_t ystride = ((uint64_t)height << 32) / outputHeight; + + #pragma omp parallel for + for(unsigned y = 0; y < outputHeight; y++) { + uint64_t yfraction = ystride * y; + uint64_t xfraction = 0; + + const uint8_t* sp = data + pitch * (yfraction >> 32); + uint8_t* dp = outputData + outputPitch * y; + + uint64_t a = read(sp); + + unsigned x = 0; + while(x < outputWidth) { + while(xfraction < 0x100000000 && x++ < outputWidth) { + write(dp, a); + dp += stride; + xfraction += xstride; + } + + sp += stride; + a = read(sp); + xfraction -= 0x100000000; + } + } + + free(); + data = outputData; + width = outputWidth; + height = outputHeight; + pitch = outputPitch; + size = height * pitch; } bool image::loadBMP(const string& filename) { diff --git a/nall/platform.hpp b/nall/platform.hpp index 8ca550e6..9c7a35d6 100644 --- a/nall/platform.hpp +++ b/nall/platform.hpp @@ -21,6 +21,7 @@ namespace Math { #include #include +#include #include #include #include diff --git a/nall/serial.hpp b/nall/serial.hpp index b821ded8..ce6bcf8f 100644 --- a/nall/serial.hpp +++ b/nall/serial.hpp @@ -5,7 +5,7 @@ #include #include -#if !defined(PLATFORM_X) && !defined(PLATFORM_OSX) +#if !defined(PLATFORM_X) && !defined(PLATFORM_MACOSX) #error "nall/serial: unsupported platform" #endif diff --git a/nall/string/platform.hpp b/nall/string/platform.hpp index 28f9a545..76429e72 100644 --- a/nall/string/platform.hpp +++ b/nall/string/platform.hpp @@ -49,7 +49,7 @@ string configpath() { SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); result = (const char*)utf8_t(path); result.transform("\\", "/"); - #elif defined(PLATFORM_OSX) + #elif defined(PLATFORM_MACOSX) result = {userpath(), "Library/Application Support/"}; #else result = {userpath(), ".config/"}; @@ -66,7 +66,7 @@ string sharedpath() { SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); result = (const char*)utf8_t(path); result.transform("\\", "/"); - #elif defined(PLATFORM_OSX) + #elif defined(PLATFORM_MACOSX) result = "/Library/Application Support/"; #else result = "/usr/share/"; diff --git a/nall/thread.hpp b/nall/thread.hpp index 3c2e876b..96536df0 100644 --- a/nall/thread.hpp +++ b/nall/thread.hpp @@ -5,7 +5,7 @@ #include #include -#if defined(PLATFORM_X) || defined(PLATFORM_OSX) +#if defined(PLATFORM_X) || defined(PLATFORM_MACOSX) #include namespace nall { @@ -64,7 +64,7 @@ void* thread_entry_point(void* parameter) { } } -#elif defined(PLATFORM_WIN) +#elif defined(PLATFORM_WINDOWS) namespace nall { inline DWORD WINAPI thread_entry_point(LPVOID); diff --git a/phoenix/Makefile b/phoenix/Makefile index d28cd50b..957e361b 100644 --- a/phoenix/Makefile +++ b/phoenix/Makefile @@ -3,7 +3,7 @@ ifeq ($(platform),) phoenixlink = else ifeq ($(platform),windows) phoenixflags = $(cppflags) $(flags) -DPHOENIX_WINDOWS - phoenixlink = -lkernel32 -luser32 -lgdi32 -ladvapi32 -lole32 -lcomctl32 -lcomdlg32 -lshlwapi + phoenixlink = -lkernel32 -luser32 -lgdi32 -ladvapi32 -lole32 -lcomctl32 -lcomdlg32 -luxtheme -lmsimg32 -lshlwapi else ifeq ($(platform),macosx) phoenixflags = $(objcppflags) $(flags) -DPHOENIX_COCOA phoenixlink = -framework Cocoa -framework Carbon diff --git a/phoenix/cocoa/action/check-item.cpp b/phoenix/cocoa/action/check-item.cpp index 9ea49464..9729dbf0 100644 --- a/phoenix/cocoa/action/check-item.cpp +++ b/phoenix/cocoa/action/check-item.cpp @@ -20,12 +20,6 @@ namespace phoenix { -bool pCheckItem::checked() { - @autoreleasepool { - return [cocoaAction state] != NSOffState; - } -} - void pCheckItem::setChecked(bool checked) { @autoreleasepool { auto state = checked ? NSOnState : NSOffState; diff --git a/phoenix/cocoa/action/check-item.hpp b/phoenix/cocoa/action/check-item.hpp index beda4ad8..9097fecb 100644 --- a/phoenix/cocoa/action/check-item.hpp +++ b/phoenix/cocoa/action/check-item.hpp @@ -12,7 +12,6 @@ struct pCheckItem : public pAction { CheckItem& checkItem; CocoaCheckItem* cocoaCheckItem = nullptr; - bool checked(); void setChecked(bool checked); void setText(string text); diff --git a/phoenix/cocoa/action/radio-item.cpp b/phoenix/cocoa/action/radio-item.cpp index 3c18eff3..39801cc3 100644 --- a/phoenix/cocoa/action/radio-item.cpp +++ b/phoenix/cocoa/action/radio-item.cpp @@ -19,12 +19,6 @@ namespace phoenix { -bool pRadioItem::checked() { - @autoreleasepool { - return [cocoaAction state] != NSOffState; - } -} - void pRadioItem::setChecked() { @autoreleasepool { for(auto& item : radioItem.state.group) { diff --git a/phoenix/cocoa/action/radio-item.hpp b/phoenix/cocoa/action/radio-item.hpp index f98ae942..62a98cd4 100644 --- a/phoenix/cocoa/action/radio-item.hpp +++ b/phoenix/cocoa/action/radio-item.hpp @@ -12,7 +12,6 @@ struct pRadioItem : public pAction { RadioItem& radioItem; CocoaRadioItem* cocoaRadioItem = nullptr; - bool checked(); void setChecked(); void setGroup(const group& group); void setText(string text); diff --git a/phoenix/cocoa/application.cpp b/phoenix/cocoa/application.cpp index ef74e66e..fbb0f787 100644 --- a/phoenix/cocoa/application.cpp +++ b/phoenix/cocoa/application.cpp @@ -84,7 +84,7 @@ void pApplication::initialize() { cocoaDelegate = [[CocoaDelegate alloc] init]; [NSApp setDelegate:cocoaDelegate]; //every window has the default application menu; call this so it is displayed at startup - [NSApp setMainMenu:[Window::none().p.cocoaWindow menu]]; + [NSApp setMainMenu:[pWindow::none().p.cocoaWindow menu]]; } } diff --git a/phoenix/cocoa/font.cpp b/phoenix/cocoa/font.cpp index 6c9e57cd..6a846c1b 100644 --- a/phoenix/cocoa/font.cpp +++ b/phoenix/cocoa/font.cpp @@ -1,19 +1,19 @@ namespace phoenix { string pFont::serif(unsigned size, string style) { - if(size == 0) size = 12; + if(size == 0) size = 8; if(style == "") style = "Normal"; return {"Georgia, ", size, ", ", style}; } string pFont::sans(unsigned size, string style) { - if(size == 0) size = 12; + if(size == 0) size = 8; if(style == "") style = "Normal"; return {"Lucida Grande, ", size, ", ", style}; } string pFont::monospace(unsigned size, string style) { - if(size == 0) size = 12; + if(size == 0) size = 8; if(style == "") style = "Normal"; return {"Menlo, ", size, ", ", style}; } @@ -32,10 +32,10 @@ NSFont* pFont::cocoaFont(string description) { NSString* family = @"Lucida Grande"; NSFontTraitMask traits = 0; - CGFloat size = 12; + CGFloat size = 8.0; if(!part(0).empty()) family = [NSString stringWithUTF8String:part(0)]; - if(!part(1).empty()) size = real(part(1)); + if(!part(1).empty()) size = decimal(part(1)); if(part(2).ifind("bold")) traits |= NSBoldFontMask; if(part(2).ifind("italic")) traits |= NSItalicFontMask; if(part(2).ifind("narrow")) traits |= NSNarrowFontMask; @@ -43,6 +43,7 @@ NSFont* pFont::cocoaFont(string description) { if(part(2).ifind("condensed")) traits |= NSCondensedFontMask; if(part(2).ifind("smallcaps")) traits |= NSSmallCapsFontMask; + size *= 1.5; //scale to point sizes (for consistency with other operating systems) return [[NSFontManager sharedFontManager] fontWithFamily:family traits:traits weight:5 size:size]; } diff --git a/phoenix/cocoa/platform.cpp b/phoenix/cocoa/platform.cpp index b5344bbc..3ae3a008 100644 --- a/phoenix/cocoa/platform.cpp +++ b/phoenix/cocoa/platform.cpp @@ -22,8 +22,10 @@ #include "widget/button.cpp" #include "widget/canvas.cpp" #include "widget/check-button.cpp" +#include "widget/check-label.cpp" #include "widget/combo-button.cpp" #include "widget/console.cpp" +#include "widget/frame.cpp" #include "widget/hex-edit.cpp" #include "widget/horizontal-scroller.cpp" #include "widget/horizontal-slider.cpp" @@ -32,6 +34,8 @@ #include "widget/list-view.cpp" #include "widget/progress-bar.cpp" #include "widget/radio-button.cpp" +#include "widget/radio-label.cpp" +#include "widget/tab-frame.cpp" #include "widget/text-edit.cpp" #include "widget/vertical-scroller.cpp" #include "widget/vertical-slider.cpp" diff --git a/phoenix/cocoa/platform.hpp b/phoenix/cocoa/platform.hpp index 297a6eb1..02c6364f 100644 --- a/phoenix/cocoa/platform.hpp +++ b/phoenix/cocoa/platform.hpp @@ -29,8 +29,10 @@ namespace phoenix { #include "widget/button.hpp" #include "widget/canvas.hpp" #include "widget/check-button.hpp" +#include "widget/check-label.hpp" #include "widget/combo-button.hpp" #include "widget/console.hpp" +#include "widget/frame.hpp" #include "widget/hex-edit.hpp" #include "widget/horizontal-scroller.hpp" #include "widget/horizontal-slider.hpp" @@ -39,6 +41,8 @@ namespace phoenix { #include "widget/list-view.hpp" #include "widget/progress-bar.hpp" #include "widget/radio-button.hpp" +#include "widget/radio-label.hpp" +#include "widget/tab-frame.hpp" #include "widget/text-edit.hpp" #include "widget/vertical-scroller.hpp" #include "widget/vertical-slider.hpp" diff --git a/phoenix/cocoa/timer.cpp b/phoenix/cocoa/timer.cpp index cb132c5d..a3eea720 100644 --- a/phoenix/cocoa/timer.cpp +++ b/phoenix/cocoa/timer.cpp @@ -19,7 +19,7 @@ } if(timer->state.enabled == false) return; instance = [NSTimer - scheduledTimerWithTimeInterval:timer->state.milliseconds / 1000.0 + scheduledTimerWithTimeInterval:timer->state.interval / 1000.0 target:self selector:@selector(run:) userInfo:nil repeats:YES ]; } @@ -38,7 +38,7 @@ void pTimer::setEnabled(bool enabled) { } } -void pTimer::setInterval(unsigned milliseconds) { +void pTimer::setInterval(unsigned interval) { @autoreleasepool { [cocoaTimer update]; } diff --git a/phoenix/cocoa/timer.hpp b/phoenix/cocoa/timer.hpp index 4466ff46..15617332 100644 --- a/phoenix/cocoa/timer.hpp +++ b/phoenix/cocoa/timer.hpp @@ -16,7 +16,7 @@ struct pTimer : public pObject { CocoaTimer* cocoaTimer = nullptr; void setEnabled(bool enabled); - void setInterval(unsigned milliseconds); + void setInterval(unsigned interval); pTimer(Timer& timer) : pObject(timer), timer(timer) {} void constructor(); diff --git a/phoenix/cocoa/widget/canvas.cpp b/phoenix/cocoa/widget/canvas.cpp index b8b12ff1..cc6fe974 100644 --- a/phoenix/cocoa/widget/canvas.cpp +++ b/phoenix/cocoa/widget/canvas.cpp @@ -4,7 +4,7 @@ if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) { canvas = &canvasReference; [self setEditable:NO]; //disable image drag-and-drop functionality - NSTrackingArea *area = [[[NSTrackingArea alloc] initWithRect:[self frame] + NSTrackingArea* area = [[[NSTrackingArea alloc] initWithRect:[self frame] options:NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect owner:self userInfo:nil ] autorelease]; @@ -25,7 +25,7 @@ } -(void) mouseButton:(NSEvent*)event down:(BOOL)isDown { - if(auto &callback = isDown ? canvas->onMousePress : canvas->onMouseRelease) { + if(auto& callback = isDown ? canvas->onMousePress : canvas->onMouseRelease) { switch([event buttonNumber]) { case 0: return callback(phoenix::Mouse::Button::Left); case 1: return callback(phoenix::Mouse::Button::Right); @@ -94,47 +94,40 @@ void pCanvas::setDroppable(bool droppable) { } } -void pCanvas::setSize(Size size) { - @autoreleasepool { - NSImage* image = [[[NSImage alloc] initWithSize:NSMakeSize(size.width, size.height)] autorelease]; - NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] - initWithBitmapDataPlanes:nil - pixelsWide:size.width pixelsHigh:size.height - bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES - isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace - bitmapFormat:NSAlphaNonpremultipliedBitmapFormat - bytesPerRow:size.width * 4 bitsPerPixel:32 - ] autorelease]; +void pCanvas::setGeometry(Geometry geometry) { + if(canvas.state.width == 0 || canvas.state.height == 0) rasterize(); - [image addRepresentation:bitmap]; - [cocoaView setImage:image]; + unsigned width = canvas.state.width; + unsigned height = canvas.state.height; + if(width == 0) width = widget.state.geometry.width; + if(height == 0) height = widget.state.geometry.height; + + if(width < geometry.width) { + geometry.x += (geometry.width - width) / 2; + geometry.width = width; } + + if(height < geometry.height) { + geometry.y += (geometry.height - height) / 2; + geometry.height = height; + } + + pWidget::setGeometry(geometry); } -void pCanvas::update() { - @autoreleasepool { - if(NSBitmapImageRep* bitmap = [[[cocoaView image] representations] objectAtIndex:0]) { - uint8_t* target = [bitmap bitmapData]; - uint32_t* source = canvas.state.data; +void pCanvas::setMode(Canvas::Mode mode) { + rasterize(), redraw(); +} - for(unsigned n = 0; n < canvas.state.width * canvas.state.height; n++) { - *target++ = *source >> 16; - *target++ = *source >> 8; - *target++ = *source >> 0; - *target++ = *source >> 24; - source++; - } - - [cocoaView setNeedsDisplay:YES]; - } - } +void pCanvas::setSize(Size size) { + rasterize(), redraw(); } void pCanvas::constructor() { @autoreleasepool { cocoaView = cocoaCanvas = [[CocoaCanvas alloc] initWith:canvas]; - setSize(canvas.size()); } + setSize(canvas.size()); } void pCanvas::destructor() { @@ -143,4 +136,79 @@ void pCanvas::destructor() { } } +void pCanvas::rasterize() { + @autoreleasepool { + unsigned width = canvas.state.width; + unsigned height = canvas.state.height; + if(width == 0) width = widget.state.geometry.width; + if(height == 0) height = widget.state.geometry.height; + + if(width != surfaceWidth || height != surfaceHeight) { + NSImage* image = [[[NSImage alloc] initWithSize:NSMakeSize(width, height)] autorelease]; + NSBitmapImageRep* bitmap = [[[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:nil + pixelsWide:width pixelsHigh:height + bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES + isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace + bitmapFormat:NSAlphaNonpremultipliedBitmapFormat + bytesPerRow:width * 4 bitsPerPixel:32 + ] autorelease]; + + [image addRepresentation:bitmap]; + [cocoaView setImage:image]; + + surfaceWidth = width; + surfaceHeight = height; + } + + if(NSBitmapImageRep* bitmap = [[[cocoaView image] representations] objectAtIndex:0]) { + uint32_t* target = (uint32_t*)[bitmap bitmapData]; + + if(canvas.state.mode == Canvas::Mode::Color) { + nall::image image; + image.allocate(width, height); + image.fill(canvas.state.color.argb()); + memcpy(target, image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Gradient) { + nall::image image; + image.allocate(width, height); + image.gradient( + canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb() + ); + memcpy(target, image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Image) { + nall::image image = canvas.state.image; + image.scale(width, height); + image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0); + memcpy(target, image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Data) { + if(width == canvas.state.width && height == canvas.state.height) { + memcpy(target, canvas.state.data, width * height * sizeof(uint32_t)); + } else { + memset(target, 0x00, width * height * sizeof(uint32_t)); + } + } + + //ARGB -> ABGR transformation + for(unsigned n = 0; n < width * height; n++) { + uint32_t color = *target; + color = (color & 0xff00ff00) | ((color & 0xff0000) >> 16) | ((color & 0x0000ff) << 16); + *target++ = color; + } + } + } +} + +void pCanvas::redraw() { + @autoreleasepool { + [cocoaView setNeedsDisplay:YES]; + } +} + } diff --git a/phoenix/cocoa/widget/canvas.hpp b/phoenix/cocoa/widget/canvas.hpp index 0ee3af87..fb728350 100644 --- a/phoenix/cocoa/widget/canvas.hpp +++ b/phoenix/cocoa/widget/canvas.hpp @@ -24,14 +24,19 @@ namespace phoenix { struct pCanvas : public pWidget { Canvas& canvas; CocoaCanvas* cocoaCanvas = nullptr; + unsigned surfaceWidth = 0; + unsigned surfaceHeight = 0; void setDroppable(bool droppable); + void setGeometry(Geometry geometry); + void setMode(Canvas::Mode mode); void setSize(Size size); - void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} void constructor(); void destructor(); + void rasterize(); + void redraw(); }; } diff --git a/phoenix/cocoa/widget/check-button.cpp b/phoenix/cocoa/widget/check-button.cpp index a91a674c..2c38f591 100644 --- a/phoenix/cocoa/widget/check-button.cpp +++ b/phoenix/cocoa/widget/check-button.cpp @@ -6,7 +6,8 @@ [self setTarget:self]; [self setAction:@selector(activate:)]; - [self setButtonType:NSSwitchButton]; + [self setBezelStyle:NSRegularSquareBezelStyle]; + [self setButtonType:NSOnOffButton]; } return self; } @@ -20,15 +21,20 @@ namespace phoenix { -bool pCheckButton::checked() { - @autoreleasepool { - return [cocoaView state] != NSOffState; - } -} - Size pCheckButton::minimumSize() { Size size = Font::size(checkButton.font(), checkButton.state.text); - return {size.width + 20, size.height}; + + if(checkButton.state.orientation == Orientation::Horizontal) { + size.width += checkButton.state.image.width; + size.height = max(checkButton.state.image.height, size.height); + } + + if(checkButton.state.orientation == Orientation::Vertical) { + size.width = max(checkButton.state.image.width, size.width); + size.height += checkButton.state.image.height; + } + + return {size.width + 20, size.height + 4}; } void pCheckButton::setChecked(bool checked) { @@ -39,11 +45,25 @@ void pCheckButton::setChecked(bool checked) { void pCheckButton::setGeometry(Geometry geometry) { pWidget::setGeometry({ - geometry.x - 2, geometry.y, - geometry.width + 4, geometry.height + geometry.x - 2, geometry.y - 2, + geometry.width + 4, geometry.height + 4 }); } +void pCheckButton::setImage(const image& image, Orientation orientation) { + @autoreleasepool { + if(image.empty()) { + [cocoaView setImage:nil]; + return; + } + + [cocoaView setImage:NSMakeImage(image)]; + + if(orientation == Orientation::Horizontal) [cocoaView setImagePosition:NSImageLeft]; + if(orientation == Orientation::Vertical ) [cocoaView setImagePosition:NSImageAbove]; + } +} + void pCheckButton::setText(string text) { @autoreleasepool { [cocoaView setTitle:[NSString stringWithUTF8String:text]]; @@ -53,8 +73,6 @@ void pCheckButton::setText(string text) { void pCheckButton::constructor() { @autoreleasepool { cocoaView = cocoaCheckButton = [[CocoaCheckButton alloc] initWith:checkButton]; - setChecked(checkButton.state.checked); - setText(checkButton.state.text); } } diff --git a/phoenix/cocoa/widget/check-button.hpp b/phoenix/cocoa/widget/check-button.hpp index 13cffc4f..11dca24b 100644 --- a/phoenix/cocoa/widget/check-button.hpp +++ b/phoenix/cocoa/widget/check-button.hpp @@ -12,10 +12,10 @@ struct pCheckButton : public pWidget { CheckButton& checkButton; CocoaCheckButton* cocoaCheckButton = nullptr; - bool checked(); Size minimumSize(); void setChecked(bool checked); void setGeometry(Geometry geometry); + void setImage(const image& image, Orientation orientation); void setText(string text); pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} diff --git a/phoenix/cocoa/widget/check-label.cpp b/phoenix/cocoa/widget/check-label.cpp new file mode 100644 index 00000000..6978d962 --- /dev/null +++ b/phoenix/cocoa/widget/check-label.cpp @@ -0,0 +1,61 @@ +@implementation CocoaCheckLabel : NSButton + +-(id) initWith:(phoenix::CheckLabel&)checkLabelReference { + if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) { + checkLabel = &checkLabelReference; + + [self setTarget:self]; + [self setAction:@selector(activate:)]; + [self setButtonType:NSSwitchButton]; + } + return self; +} + +-(IBAction) activate:(id)sender { + checkLabel->state.checked = [self state] != NSOffState; + if(checkLabel->onToggle) checkLabel->onToggle(); +} + +@end + +namespace phoenix { + +Size pCheckLabel::minimumSize() { + Size size = Font::size(checkLabel.font(), checkLabel.state.text); + return {size.width + 20, size.height}; +} + +void pCheckLabel::setChecked(bool checked) { + @autoreleasepool { + [cocoaView setState:checked ? NSOnState : NSOffState]; + } +} + +void pCheckLabel::setGeometry(Geometry geometry) { + pWidget::setGeometry({ + geometry.x - 2, geometry.y, + geometry.width + 4, geometry.height + }); +} + +void pCheckLabel::setText(string text) { + @autoreleasepool { + [cocoaView setTitle:[NSString stringWithUTF8String:text]]; + } +} + +void pCheckLabel::constructor() { + @autoreleasepool { + cocoaView = cocoaCheckLabel = [[CocoaCheckLabel alloc] initWith:checkLabel]; + setChecked(checkLabel.state.checked); + setText(checkLabel.state.text); + } +} + +void pCheckLabel::destructor() { + @autoreleasepool { + [cocoaView release]; + } +} + +} diff --git a/phoenix/cocoa/widget/check-label.hpp b/phoenix/cocoa/widget/check-label.hpp new file mode 100644 index 00000000..4fdcf659 --- /dev/null +++ b/phoenix/cocoa/widget/check-label.hpp @@ -0,0 +1,25 @@ +@interface CocoaCheckLabel : NSButton { +@public + phoenix::CheckLabel* checkLabel; +} +-(id) initWith:(phoenix::CheckLabel&)checkLabel; +-(IBAction) activate:(id)sender; +@end + +namespace phoenix { + +struct pCheckLabel : public pWidget { + CheckLabel& checkLabel; + CocoaCheckLabel* cocoaCheckLabel = nullptr; + + Size minimumSize(); + void setChecked(bool checked); + void setGeometry(Geometry geometry); + void setText(string text); + + pCheckLabel(CheckLabel& checkLabel) : pWidget(checkLabel), checkLabel(checkLabel) {} + void constructor(); + void destructor(); +}; + +} diff --git a/phoenix/cocoa/widget/combo-button.cpp b/phoenix/cocoa/widget/combo-button.cpp index 4287caa8..47330096 100644 --- a/phoenix/cocoa/widget/combo-button.cpp +++ b/phoenix/cocoa/widget/combo-button.cpp @@ -11,6 +11,7 @@ } -(IBAction) activate:(id)sender { + comboButton->state.selection = [self indexOfSelectedItem]; if(comboButton->onChange) comboButton->onChange(); } @@ -31,15 +32,9 @@ Size pComboButton::minimumSize() { return {maximumWidth + 36, size.height + 6}; } -void pComboButton::modify(unsigned row, string text) { +void pComboButton::remove(unsigned selection) { @autoreleasepool { - [[cocoaView itemAtIndex:row] setTitle:[NSString stringWithUTF8String:text]]; - } -} - -void pComboButton::remove(unsigned row) { - @autoreleasepool { - [cocoaView removeItemAtIndex:row]; + [cocoaView removeItemAtIndex:selection]; } } @@ -49,12 +44,6 @@ void pComboButton::reset() { } } -unsigned pComboButton::selection() { - @autoreleasepool { - return [cocoaView indexOfSelectedItem]; - } -} - void pComboButton::setGeometry(Geometry geometry) { pWidget::setGeometry({ geometry.x - 2, geometry.y, @@ -62,9 +51,15 @@ void pComboButton::setGeometry(Geometry geometry) { }); } -void pComboButton::setSelection(unsigned row) { +void pComboButton::setSelection(unsigned selection) { @autoreleasepool { - [cocoaView selectItemAtIndex:row]; + [cocoaView selectItemAtIndex:selection]; + } +} + +void pComboButton::setText(unsigned selection, string text) { + @autoreleasepool { + [[cocoaView itemAtIndex:selection] setTitle:[NSString stringWithUTF8String:text]]; } } diff --git a/phoenix/cocoa/widget/combo-button.hpp b/phoenix/cocoa/widget/combo-button.hpp index 27f8732e..500f17cc 100644 --- a/phoenix/cocoa/widget/combo-button.hpp +++ b/phoenix/cocoa/widget/combo-button.hpp @@ -14,12 +14,11 @@ struct pComboButton : public pWidget { void append(string text); Size minimumSize(); - void modify(unsigned row, string text); - void remove(unsigned row); + void remove(unsigned selection); void reset(); - unsigned selection(); void setGeometry(Geometry geometry); - void setSelection(unsigned row); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {} void constructor(); diff --git a/phoenix/cocoa/widget/frame.cpp b/phoenix/cocoa/widget/frame.cpp new file mode 100644 index 00000000..ebf6b96b --- /dev/null +++ b/phoenix/cocoa/widget/frame.cpp @@ -0,0 +1,68 @@ +@implementation CocoaFrame : NSBox + +-(id) initWith:(phoenix::Frame&)frameReference { + if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) { + frame = &frameReference; + + [self setTitle:@""]; + } + return self; +} + +@end + +namespace phoenix { + +void pFrame::setEnabled(bool enabled) { + if(frame.state.layout) frame.state.layout->setEnabled(frame.state.layout->enabled()); + pWidget::setEnabled(enabled); +} + +void pFrame::setFont(string font) { + @autoreleasepool { + [cocoaView setTitleFont:pFont::cocoaFont(font)]; + } +} + +void pFrame::setGeometry(Geometry geometry) { + bool empty = frame.state.text.empty(); + Size size = Font::size(frame.font(), frame.state.text); + pWidget::setGeometry({ + geometry.x - 3, geometry.y - (empty ? size.height - 2 : 1), + geometry.width + 6, geometry.height + (empty ? size.height + 2 : 5) + }); + if(frame.state.layout == nullptr) return; + geometry.x += 1, geometry.y += (empty ? 1 : size.height - 2); + geometry.width -= 2, geometry.height -= (empty ? 1 : size.height - 1); + frame.state.layout->setGeometry(geometry); +} + +void pFrame::setText(string text) { + @autoreleasepool { + [cocoaView setTitle:[NSString stringWithUTF8String:text]]; + } +} + +void pFrame::setVisible(bool visible) { + if(frame.state.layout) frame.state.layout->setVisible(frame.state.layout->visible()); + pWidget::setVisible(visible); +} + +void pFrame::constructor() { + @autoreleasepool { + cocoaView = cocoaFrame = [[CocoaFrame alloc] initWith:frame]; + } +} + +void pFrame::destructor() { + @autoreleasepool { + [cocoaView release]; + } +} + +void pFrame::orphan() { + destructor(); + constructor(); +} + +} diff --git a/phoenix/cocoa/widget/frame.hpp b/phoenix/cocoa/widget/frame.hpp new file mode 100644 index 00000000..b5f220c2 --- /dev/null +++ b/phoenix/cocoa/widget/frame.hpp @@ -0,0 +1,26 @@ +@interface CocoaFrame : NSBox { +@public + phoenix::Frame* frame; +} +-(id) initWith:(phoenix::Frame&)frame; +@end + +namespace phoenix { + +struct pFrame : public pWidget { + Frame& frame; + CocoaFrame* cocoaFrame = nullptr; + + void setEnabled(bool enabled); + void setFont(string font); + void setGeometry(Geometry geometry); + void setText(string text); + void setVisible(bool visible); + + pFrame(Frame& frame) : pWidget(frame), frame(frame) {} + void constructor(); + void destructor(); + void orphan(); +}; + +} diff --git a/phoenix/cocoa/widget/horizontal-scroller.cpp b/phoenix/cocoa/widget/horizontal-scroller.cpp index 19498166..d91770cd 100644 --- a/phoenix/cocoa/widget/horizontal-scroller.cpp +++ b/phoenix/cocoa/widget/horizontal-scroller.cpp @@ -58,12 +58,6 @@ Size pHorizontalScroller::minimumSize() { } } -unsigned pHorizontalScroller::position() { - @autoreleasepool { - return [cocoaView doubleValue] * horizontalScroller.state.length; - } -} - void pHorizontalScroller::setLength(unsigned length) { @autoreleasepool { [cocoaView update]; diff --git a/phoenix/cocoa/widget/horizontal-scroller.hpp b/phoenix/cocoa/widget/horizontal-scroller.hpp index c62925f3..1603edca 100644 --- a/phoenix/cocoa/widget/horizontal-scroller.hpp +++ b/phoenix/cocoa/widget/horizontal-scroller.hpp @@ -14,7 +14,6 @@ struct pHorizontalScroller : public pWidget { CocoaHorizontalScroller* cocoaHorizontalScroller = nullptr; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/cocoa/widget/horizontal-slider.cpp b/phoenix/cocoa/widget/horizontal-slider.cpp index 62be0c8d..ca801a4c 100644 --- a/phoenix/cocoa/widget/horizontal-slider.cpp +++ b/phoenix/cocoa/widget/horizontal-slider.cpp @@ -24,12 +24,6 @@ Size pHorizontalSlider::minimumSize() { return {48, 20}; } -unsigned pHorizontalSlider::position() { - @autoreleasepool { - return [cocoaView doubleValue]; - } -} - void pHorizontalSlider::setGeometry(Geometry geometry) { pWidget::setGeometry({ geometry.x - 2, geometry.y, diff --git a/phoenix/cocoa/widget/horizontal-slider.hpp b/phoenix/cocoa/widget/horizontal-slider.hpp index dc7a5cd3..595d7521 100644 --- a/phoenix/cocoa/widget/horizontal-slider.hpp +++ b/phoenix/cocoa/widget/horizontal-slider.hpp @@ -13,7 +13,6 @@ struct pHorizontalSlider : public pWidget { CocoaHorizontalSlider* cocoaHorizontalSlider = nullptr; Size minimumSize(); - unsigned position(); void setGeometry(Geometry geometry); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/cocoa/widget/list-view.cpp b/phoenix/cocoa/widget/list-view.cpp index 88202f96..a3cfcc52 100644 --- a/phoenix/cocoa/widget/list-view.cpp +++ b/phoenix/cocoa/widget/list-view.cpp @@ -134,6 +134,8 @@ } -(void) tableViewSelectionDidChange:(NSNotification*)notification { + listView->state.selected = true; + listView->state.selection = [content selectedRow]; if(listView->onChange) listView->onChange(); } @@ -178,14 +180,13 @@ unsigned textDisplacement = 0; if(image) { - NSGraphicsContext* context = [NSGraphicsContext currentContext]; - [context saveGraphicsState]; + [[NSGraphicsContext currentContext] saveGraphicsState]; NSRect targetRect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.height, frame.size.height); NSRect sourceRect = NSMakeRect(0, 0, [image size].width, [image size].height); [image drawInRect:targetRect fromRect:sourceRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; - [context restoreGraphicsState]; + [[NSGraphicsContext currentContext] restoreGraphicsState]; textDisplacement = frame.size.height + 2; } @@ -237,17 +238,7 @@ void pListView::autoSizeColumns() { } } -bool pListView::checked(unsigned row) { - return listView.state.checked(row); -} - -void pListView::modify(unsigned row, const lstring& text) { - @autoreleasepool { - [[cocoaView content] reloadData]; - } -} - -void pListView::remove(unsigned row) { +void pListView::remove(unsigned selection) { @autoreleasepool { [[cocoaView content] reloadData]; } @@ -259,27 +250,13 @@ void pListView::reset() { } } -bool pListView::selected() { - @autoreleasepool { - return [[cocoaView content] selectedRow] >= 0; - } -} - -unsigned pListView::selection() { - if(selected() == false) return 0; - - @autoreleasepool { - return [[cocoaView content] selectedRow]; - } -} - void pListView::setCheckable(bool checkable) { @autoreleasepool { [cocoaView reloadColumns]; } } -void pListView::setChecked(unsigned row, bool checked) { +void pListView::setChecked(unsigned selection, bool checked) { @autoreleasepool { [[cocoaView content] reloadData]; } @@ -307,7 +284,7 @@ void pListView::setHeaderVisible(bool visible) { } } -void pListView::setImage(unsigned row, unsigned column, const image& image) { +void pListView::setImage(unsigned selection, unsigned position, const image& image) { @autoreleasepool { [[cocoaView content] reloadData]; } @@ -321,9 +298,15 @@ void pListView::setSelected(bool selected) { } } -void pListView::setSelection(unsigned row) { +void pListView::setSelection(unsigned selection) { @autoreleasepool { - [[cocoaView content] selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(row, 1)] byExtendingSelection:NO]; + [[cocoaView content] selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(selection, 1)] byExtendingSelection:NO]; + } +} + +void pListView::setText(unsigned selection, unsigned position, const string text) { + @autoreleasepool { + [[cocoaView content] reloadData]; } } diff --git a/phoenix/cocoa/widget/list-view.hpp b/phoenix/cocoa/widget/list-view.hpp index a94d8e0f..be631719 100644 --- a/phoenix/cocoa/widget/list-view.hpp +++ b/phoenix/cocoa/widget/list-view.hpp @@ -42,20 +42,17 @@ struct pListView : public pWidget { void append(const lstring& text); void autoSizeColumns(); - bool checked(unsigned row); - void modify(unsigned row, const lstring& text); - void remove(unsigned row); + void remove(unsigned selection); void reset(); - bool selected(); - unsigned selection(); void setCheckable(bool checkable); - void setChecked(unsigned row, bool checked); + void setChecked(unsigned selection, bool checked); void setFont(string font); void setHeaderText(const lstring& text); void setHeaderVisible(bool visible); - void setImage(unsigned row, unsigned column, const image& image); + void setImage(unsigned selection, unsigned position, const image& image); void setSelected(bool selected); - void setSelection(unsigned row); + void setSelection(unsigned selection); + void setText(unsigned selection, unsigned position, string text); pListView(ListView& listView) : pWidget(listView), listView(listView) {} void constructor(); diff --git a/phoenix/cocoa/widget/radio-button.cpp b/phoenix/cocoa/widget/radio-button.cpp index 775a0b00..bf387e3a 100644 --- a/phoenix/cocoa/widget/radio-button.cpp +++ b/phoenix/cocoa/widget/radio-button.cpp @@ -6,29 +6,38 @@ [self setTarget:self]; [self setAction:@selector(activate:)]; - [self setButtonType:NSRadioButton]; + [self setBezelStyle:NSRegularSquareBezelStyle]; + [self setButtonType:NSOnOffButton]; } return self; } -(IBAction) activate:(id)sender { + bool wasChecked = radioButton->state.checked; radioButton->setChecked(); - if(radioButton->onActivate) radioButton->onActivate(); + if(wasChecked == false) { + if(radioButton->onActivate) radioButton->onActivate(); + } } @end namespace phoenix { -bool pRadioButton::checked() { - @autoreleasepool { - return [cocoaView state] != NSOffState; - } -} - Size pRadioButton::minimumSize() { Size size = Font::size(radioButton.font(), radioButton.state.text); - return {size.width + 22, size.height}; + + if(radioButton.state.orientation == Orientation::Horizontal) { + size.width += radioButton.state.image.width; + size.height = max(radioButton.state.image.height, size.height); + } + + if(radioButton.state.orientation == Orientation::Vertical) { + size.width = max(radioButton.state.image.width, size.width); + size.height += radioButton.state.image.height; + } + + return {size.width + 20, size.height + 4}; } void pRadioButton::setChecked() { @@ -42,14 +51,28 @@ void pRadioButton::setChecked() { void pRadioButton::setGeometry(Geometry geometry) { pWidget::setGeometry({ - geometry.x - 1, geometry.y, - geometry.width + 2, geometry.height + geometry.x - 2, geometry.y - 2, + geometry.width + 4, geometry.height + 4 }); } void pRadioButton::setGroup(const group& group) { } +void pRadioButton::setImage(const image& image, Orientation orientation) { + @autoreleasepool { + if(image.empty()) { + [cocoaView setImage:nil]; + return; + } + + [cocoaView setImage:NSMakeImage(image)]; + + if(orientation == Orientation::Horizontal) [cocoaView setImagePosition:NSImageLeft]; + if(orientation == Orientation::Vertical ) [cocoaView setImagePosition:NSImageAbove]; + } +} + void pRadioButton::setText(string text) { @autoreleasepool { [cocoaView setTitle:[NSString stringWithUTF8String:text]]; diff --git a/phoenix/cocoa/widget/radio-button.hpp b/phoenix/cocoa/widget/radio-button.hpp index 5c8ab665..0242ddaa 100644 --- a/phoenix/cocoa/widget/radio-button.hpp +++ b/phoenix/cocoa/widget/radio-button.hpp @@ -12,11 +12,11 @@ struct pRadioButton : public pWidget { RadioButton& radioButton; CocoaRadioButton* cocoaRadioButton = nullptr; - bool checked(); Size minimumSize(); void setChecked(); void setGeometry(Geometry geometry); void setGroup(const group& group); + void setImage(const image& image, Orientation orientation); void setText(string text); pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {} diff --git a/phoenix/cocoa/widget/radio-label.cpp b/phoenix/cocoa/widget/radio-label.cpp new file mode 100644 index 00000000..b677dd35 --- /dev/null +++ b/phoenix/cocoa/widget/radio-label.cpp @@ -0,0 +1,65 @@ +@implementation CocoaRadioLabel : NSButton + +-(id) initWith:(phoenix::RadioLabel&)radioLabelReference { + if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) { + radioLabel = &radioLabelReference; + + [self setTarget:self]; + [self setAction:@selector(activate:)]; + [self setButtonType:NSRadioButton]; + } + return self; +} + +-(IBAction) activate:(id)sender { + radioLabel->setChecked(); + if(radioLabel->onActivate) radioLabel->onActivate(); +} + +@end + +namespace phoenix { + +Size pRadioLabel::minimumSize() { + Size size = Font::size(radioLabel.font(), radioLabel.state.text); + return {size.width + 22, size.height}; +} + +void pRadioLabel::setChecked() { + @autoreleasepool { + for(auto& item : radioLabel.state.group) { + auto state = (&item == &radioLabel) ? NSOnState : NSOffState; + [item.p.cocoaView setState:state]; + } + } +} + +void pRadioLabel::setGeometry(Geometry geometry) { + pWidget::setGeometry({ + geometry.x - 1, geometry.y, + geometry.width + 2, geometry.height + }); +} + +void pRadioLabel::setGroup(const group& group) { +} + +void pRadioLabel::setText(string text) { + @autoreleasepool { + [cocoaView setTitle:[NSString stringWithUTF8String:text]]; + } +} + +void pRadioLabel::constructor() { + @autoreleasepool { + cocoaView = cocoaRadioLabel = [[CocoaRadioLabel alloc] initWith:radioLabel]; + } +} + +void pRadioLabel::destructor() { + @autoreleasepool { + [cocoaView release]; + } +} + +} diff --git a/phoenix/cocoa/widget/radio-label.hpp b/phoenix/cocoa/widget/radio-label.hpp new file mode 100644 index 00000000..c971683c --- /dev/null +++ b/phoenix/cocoa/widget/radio-label.hpp @@ -0,0 +1,26 @@ +@interface CocoaRadioLabel : NSButton { +@public + phoenix::RadioLabel* radioLabel; +} +-(id) initWith:(phoenix::RadioLabel&)radioLabel; +-(IBAction) activate:(id)sender; +@end + +namespace phoenix { + +struct pRadioLabel : public pWidget { + RadioLabel& radioLabel; + CocoaRadioLabel* cocoaRadioLabel = nullptr; + + Size minimumSize(); + void setChecked(); + void setGeometry(Geometry geometry); + void setGroup(const group& group); + void setText(string text); + + pRadioLabel(RadioLabel& radioLabel) : pWidget(radioLabel), radioLabel(radioLabel) {} + void constructor(); + void destructor(); +}; + +} diff --git a/phoenix/cocoa/widget/tab-frame.cpp b/phoenix/cocoa/widget/tab-frame.cpp new file mode 100644 index 00000000..dd44620d --- /dev/null +++ b/phoenix/cocoa/widget/tab-frame.cpp @@ -0,0 +1,145 @@ +@implementation CocoaTabFrame : NSTabView + +-(id) initWith:(phoenix::TabFrame&)tabFrameReference { + if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) { + tabFrame = &tabFrameReference; + + [self setDelegate:self]; + } + return self; +} + +-(void) tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem { + tabFrame->state.selection = [tabView indexOfTabViewItem:tabViewItem]; + tabFrame->p.synchronizeLayout(); + if(tabFrame->onChange) tabFrame->onChange(); +} + +@end + +@implementation CocoaTabFrameItem : NSTabViewItem + +-(id) initWith:(phoenix::TabFrame&)tabFrameReference { + if(self = [super initWithIdentifier:nil]) { + tabFrame = &tabFrameReference; + cocoaTabFrame = tabFrame->p.cocoaTabFrame; + } + return self; +} + +-(NSSize) sizeOfLabel:(BOOL)shouldTruncateLabel { + NSSize sizeOfLabel = [super sizeOfLabel:shouldTruncateLabel]; + signed selection = [cocoaTabFrame indexOfTabViewItem:self]; + if(selection < 0) return sizeOfLabel; //should never happen + if(tabFrame->state.image[selection].empty() == false) { + unsigned iconSize = phoenix::Font::size(tabFrame->font(), " ").height; + sizeOfLabel.width += iconSize + 2; + } + return sizeOfLabel; +} + +-(void) drawLabel:(BOOL)shouldTruncateLabel inRect:(NSRect)tabRect { + signed selection = [cocoaTabFrame indexOfTabViewItem:self]; + if(selection >= 0 && tabFrame->state.image[selection].empty() == false) { + unsigned iconSize = phoenix::Font::size(tabFrame->font(), " ").height; + NSImage* image = NSMakeImage(tabFrame->state.image[selection]); + + [[NSGraphicsContext currentContext] saveGraphicsState]; + NSRect targetRect = NSMakeRect(tabRect.origin.x, tabRect.origin.y + 2, iconSize, iconSize); + [image drawInRect:targetRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; + [[NSGraphicsContext currentContext] restoreGraphicsState]; + + tabRect.origin.x += iconSize + 2; + tabRect.size.width -= iconSize + 2; + } + [super drawLabel:shouldTruncateLabel inRect:tabRect]; +} + +@end + +namespace phoenix { + +void pTabFrame::append(string text, const image& image) { + @autoreleasepool { + CocoaTabFrameItem* item = [[CocoaTabFrameItem alloc] initWith:tabFrame]; + [item setLabel:[NSString stringWithUTF8String:text]]; + [cocoaView addTabViewItem:item]; + tabs.append(item); + } +} + +void pTabFrame::remove(unsigned selection) { + @autoreleasepool { + CocoaTabFrameItem* item = tabs[selection]; + [cocoaView removeTabViewItem:item]; + tabs.remove(selection); + } +} + +void pTabFrame::setEnabled(bool enabled) { + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setEnabled(layout->enabled()); + } + pWidget::setEnabled(enabled); +} + +void pTabFrame::setGeometry(Geometry geometry) { + pWidget::setGeometry({ + geometry.x - 7, geometry.y - 5, + geometry.width + 14, geometry.height + 6 + }); + geometry.x += 1, geometry.width -= 2; + geometry.y += 22, geometry.height -= 32; + for(auto& layout : tabFrame.state.layout) { + if(layout == nullptr) continue; + layout->setGeometry(geometry); + } + synchronizeLayout(); +} + +void pTabFrame::setImage(unsigned selection, const image& image) { +} + +void pTabFrame::setSelection(unsigned selection) { + @autoreleasepool { + CocoaTabFrameItem* item = tabs[selection]; + [cocoaView selectTabViewItem:item]; + } + synchronizeLayout(); +} + +void pTabFrame::setText(unsigned selection, string text) { + @autoreleasepool { + CocoaTabFrameItem* item = tabs[selection]; + [item setLabel:[NSString stringWithUTF8String:text]]; + } +} + +void pTabFrame::setVisible(bool visible) { + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setVisible(layout->visible()); + } + pWidget::setVisible(visible); +} + +void pTabFrame::constructor() { + @autoreleasepool { + cocoaView = cocoaTabFrame = [[CocoaTabFrame alloc] initWith:tabFrame]; + } +} + +void pTabFrame::destructor() { + @autoreleasepool { + [cocoaView release]; + } +} + +void pTabFrame::synchronizeLayout() { + unsigned selection = 0; + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setVisible(selection == tabFrame.state.selection); + selection++; + } +} + +} diff --git a/phoenix/cocoa/widget/tab-frame.hpp b/phoenix/cocoa/widget/tab-frame.hpp new file mode 100644 index 00000000..e1891060 --- /dev/null +++ b/phoenix/cocoa/widget/tab-frame.hpp @@ -0,0 +1,41 @@ +@interface CocoaTabFrame : NSTabView { +@public + phoenix::TabFrame* tabFrame; +} +-(id) initWith:(phoenix::TabFrame&)tabFrame; +-(void) tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem; +@end + +@interface CocoaTabFrameItem : NSTabViewItem { +@public + phoenix::TabFrame* tabFrame; + CocoaTabFrame* cocoaTabFrame; +} +-(id) initWith:(phoenix::TabFrame&)tabFrame; +-(NSSize) sizeOfLabel:(BOOL)shouldTruncateLabel; +-(void) drawLabel:(BOOL)shouldTruncateLabel inRect:(NSRect)tabRect; +@end + +namespace phoenix { + +struct pTabFrame : public pWidget { + TabFrame& tabFrame; + CocoaTabFrame* cocoaTabFrame = nullptr; + vector tabs; + + void append(string text, const image& image); + void remove(unsigned selection); + void setEnabled(bool enabled); + void setGeometry(Geometry geometry); + void setImage(unsigned selection, const image& image); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); + void setVisible(bool visible); + + pTabFrame(TabFrame& tabFrame) : pWidget(tabFrame), tabFrame(tabFrame) {} + void constructor(); + void destructor(); + void synchronizeLayout(); +}; + +} diff --git a/phoenix/cocoa/widget/vertical-scroller.cpp b/phoenix/cocoa/widget/vertical-scroller.cpp index 7591a7d2..6e98e383 100644 --- a/phoenix/cocoa/widget/vertical-scroller.cpp +++ b/phoenix/cocoa/widget/vertical-scroller.cpp @@ -58,12 +58,6 @@ Size pVerticalScroller::minimumSize() { } } -unsigned pVerticalScroller::position() { - @autoreleasepool { - return [cocoaView doubleValue] * verticalScroller.state.length; - } -} - void pVerticalScroller::setLength(unsigned length) { @autoreleasepool { [cocoaView update]; diff --git a/phoenix/cocoa/widget/vertical-scroller.hpp b/phoenix/cocoa/widget/vertical-scroller.hpp index b7bc9796..00129538 100644 --- a/phoenix/cocoa/widget/vertical-scroller.hpp +++ b/phoenix/cocoa/widget/vertical-scroller.hpp @@ -14,7 +14,6 @@ struct pVerticalScroller : public pWidget { CocoaVerticalScroller* cocoaVerticalScroller = nullptr; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/cocoa/widget/vertical-slider.cpp b/phoenix/cocoa/widget/vertical-slider.cpp index be75778d..264ba444 100644 --- a/phoenix/cocoa/widget/vertical-slider.cpp +++ b/phoenix/cocoa/widget/vertical-slider.cpp @@ -24,12 +24,6 @@ Size pVerticalSlider::minimumSize() { return {20, 48}; } -unsigned pVerticalSlider::position() { - @autoreleasepool { - return [cocoaView doubleValue]; - } -} - void pVerticalSlider::setGeometry(Geometry geometry) { pWidget::setGeometry({ geometry.x, geometry.y - 2, diff --git a/phoenix/cocoa/widget/vertical-slider.hpp b/phoenix/cocoa/widget/vertical-slider.hpp index 977f834c..ecec74ad 100644 --- a/phoenix/cocoa/widget/vertical-slider.hpp +++ b/phoenix/cocoa/widget/vertical-slider.hpp @@ -13,7 +13,6 @@ struct pVerticalSlider : public pWidget { CocoaVerticalSlider* cocoaVerticalSlider = nullptr; Size minimumSize(); - unsigned position(); void setGeometry(Geometry geometry); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/cocoa/widget/widget.cpp b/phoenix/cocoa/widget/widget.cpp index e527871e..c2f7ed5e 100644 --- a/phoenix/cocoa/widget/widget.cpp +++ b/phoenix/cocoa/widget/widget.cpp @@ -17,8 +17,9 @@ Size pWidget::minimumSize() { } void pWidget::setEnabled(bool enabled) { + if(!widget.parent()) enabled = false; if(widget.state.abstract) enabled = false; - if(sizable.state.layout && sizable.state.layout->enabled() == false) enabled = false; + if(!widget.enabledToAll()) enabled = false; @autoreleasepool { if([cocoaView respondsToSelector:@selector(setEnabled:)]) { @@ -47,11 +48,13 @@ void pWidget::setGeometry(Geometry geometry) { [cocoaView setFrame:NSMakeRect(geometry.x, windowHeight - geometry.y - geometry.height, geometry.width, geometry.height)]; [[cocoaView superview] setNeedsDisplay:YES]; } + if(widget.onSize) widget.onSize(); } void pWidget::setVisible(bool visible) { + if(!widget.parent()) visible = false; if(widget.state.abstract) visible = false; - if(sizable.state.layout && sizable.state.layout->visible() == false) visible = false; + if(!widget.visibleToAll()) visible = false; @autoreleasepool { [cocoaView setHidden:!visible]; diff --git a/phoenix/cocoa/widget/widget.hpp b/phoenix/cocoa/widget/widget.hpp index caf4e4d1..c4e9c394 100644 --- a/phoenix/cocoa/widget/widget.hpp +++ b/phoenix/cocoa/widget/widget.hpp @@ -7,11 +7,11 @@ struct pWidget : public pSizable { bool enabled(); bool focused(); virtual Size minimumSize(); - void setEnabled(bool enabled); + virtual void setEnabled(bool enabled); void setFocused(); virtual void setFont(string font); virtual void setGeometry(Geometry geometry); - void setVisible(bool visible); + virtual void setVisible(bool visible); pWidget(Widget& widget) : pSizable(widget), widget(widget) {} void constructor(); diff --git a/phoenix/cocoa/window.cpp b/phoenix/cocoa/window.cpp index 0f796301..b6c39978 100644 --- a/phoenix/cocoa/window.cpp +++ b/phoenix/cocoa/window.cpp @@ -170,24 +170,12 @@ void pWindow::append(Widget& widget) { @autoreleasepool { [widget.p.cocoaView removeFromSuperview]; - [[cocoaWindow contentView] addSubview:widget.p.cocoaView positioned:NSWindowBelow relativeTo:nil]; + [[cocoaWindow contentView] addSubview:widget.p.cocoaView positioned:NSWindowAbove relativeTo:nil]; widget.p.setGeometry(widget.geometry()); [[cocoaWindow contentView] setNeedsDisplay:YES]; } } -Color pWindow::backgroundColor() { - @autoreleasepool { - NSColor* color = [[cocoaWindow backgroundColor] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; - return { - uint8_t(255 * [color redComponent]), - uint8_t(255 * [color greenComponent]), - uint8_t(255 * [color blueComponent]), - uint8_t(255 * [color alphaComponent]) - }; - } -} - bool pWindow::focused() { @autoreleasepool { return [cocoaWindow isMainWindow] == YES; @@ -362,6 +350,14 @@ void pWindow::setWidgetFont(string font) { void pWindow::constructor() { @autoreleasepool { cocoaWindow = [[CocoaWindow alloc] initWith:window]; + + NSColor* color = [[cocoaWindow backgroundColor] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + window.state.backgroundColor = Color( + (uint8_t)(255 * [color redComponent]), + (uint8_t)(255 * [color greenComponent]), + (uint8_t)(255 * [color blueComponent]), + (uint8_t)(255 * [color alphaComponent]) + ); } } diff --git a/phoenix/cocoa/window.hpp b/phoenix/cocoa/window.hpp index 3c0bafe4..bac1818a 100644 --- a/phoenix/cocoa/window.hpp +++ b/phoenix/cocoa/window.hpp @@ -32,7 +32,6 @@ struct pWindow : public pObject { void append(Layout& layout); void append(Menu& menu); void append(Widget& widget); - Color backgroundColor(); bool focused(); Geometry frameMargin(); Geometry geometry(); diff --git a/phoenix/core/core.cpp b/phoenix/core/core.cpp index 4c083485..38127f24 100644 --- a/phoenix/core/core.cpp +++ b/phoenix/core/core.cpp @@ -15,6 +15,7 @@ using namespace nall; namespace phoenix { #include "state.hpp" + #include "utility.cpp" #include "layout/fixed-layout.cpp" #include "layout/horizontal-layout.cpp" #include "layout/vertical-layout.cpp" @@ -64,7 +65,7 @@ void Application::quit() { return pApplication::quit(); } -void Application::setName(string name) { +void Application::setName(const string& name) { applicationState.name = name; } @@ -83,7 +84,7 @@ uint32_t Color::rgb() const { return (255 << 24) + (red << 16) + (green << 8) + (blue << 0); } -uint32_t Color::rgba() const { +uint32_t Color::argb() const { return (alpha << 24) + (red << 16) + (green << 8) + (blue << 0); } @@ -102,7 +103,7 @@ string Geometry::text() const { return {x, ",", y, ",", width, ",", height}; } -Geometry::Geometry(string text) { +Geometry::Geometry(const string& text) { lstring part = text.split(","); x = integer(part(0, "256")); y = integer(part(1, "256")); @@ -113,19 +114,19 @@ Geometry::Geometry(string text) { //Font //==== -string Font::serif(unsigned size, string style) { +string Font::serif(unsigned size, const string& style) { return pFont::serif(size, style); } -string Font::sans(unsigned size, string style) { +string Font::sans(unsigned size, const string& style) { return pFont::sans(size, style); } -string Font::monospace(unsigned size, string style) { +string Font::monospace(unsigned size, const string& style) { return pFont::monospace(size, style); } -Size Font::size(string font, string text) { +Size Font::size(const string& font, const string& text) { return pFont::size(font, text); } @@ -185,7 +186,7 @@ string BrowserWindow::save() { return pBrowserWindow::save(state); } -BrowserWindow& BrowserWindow::setFilters_(const lstring& filters) { +BrowserWindow& BrowserWindow::setFilters(const lstring& filters) { state.filters = filters; return *this; } @@ -195,12 +196,12 @@ BrowserWindow& BrowserWindow::setParent(Window& parent) { return *this; } -BrowserWindow& BrowserWindow::setPath(string path) { +BrowserWindow& BrowserWindow::setPath(const string& path) { state.path = path; return *this; } -BrowserWindow& BrowserWindow::setTitle(string title) { +BrowserWindow& BrowserWindow::setTitle(const string& title) { state.title = title; return *this; } @@ -236,12 +237,12 @@ MessageWindow& MessageWindow::setParent(Window& parent) { return *this; } -MessageWindow& MessageWindow::setText(string text) { +MessageWindow& MessageWindow::setText(const string& text) { state.text = text; return *this; } -MessageWindow& MessageWindow::setTitle(string title) { +MessageWindow& MessageWindow::setTitle(const string& title) { state.title = title; return *this; } @@ -251,7 +252,7 @@ MessageWindow::Response MessageWindow::warning(MessageWindow::Buttons buttons) { return pMessageWindow::warning(state); } -MessageWindow::MessageWindow(string text): +MessageWindow::MessageWindow(const string& text): state(*new State) { state.text = text; } @@ -277,14 +278,22 @@ Object::~Object() { //Timer //===== +bool Timer::enabled() const { + return state.enabled; +} + +unsigned Timer::interval() const { + return state.interval; +} + void Timer::setEnabled(bool enabled) { state.enabled = enabled; return p.setEnabled(enabled); } -void Timer::setInterval(unsigned milliseconds) { - state.milliseconds = milliseconds; - return p.setInterval(milliseconds); +void Timer::setInterval(unsigned interval) { + state.interval = interval; + return p.setInterval(interval); } Timer::Timer(): @@ -303,35 +312,36 @@ Timer::~Timer() { //Window //====== -Window& Window::none() { - return pWindow::none(); -} - -void Window::append_(Layout& layout) { +void Window::append(Layout& layout) { if(state.layout.append(layout)) { - ((Sizable&)layout).state.window = this; - ((Sizable&)layout).state.layout = nullptr; + layout.Sizable::state.parent = nullptr; + layout.Sizable::state.window = this; p.append(layout); layout.synchronizeLayout(); } } -void Window::append_(Menu& menu) { +void Window::append(Menu& menu) { if(state.menu.append(menu)) { - ((Action&)menu).state.window = this; + menu.Action::state.window = this; p.append(menu); } } -void Window::append_(Widget& widget) { +void Window::append(Widget& widget) { if(state.widget.append(widget)) { - ((Sizable&)widget).state.window = this; + widget.Sizable::state.window = this; p.append(widget); + widget.synchronizeLayout(); } } -Color Window::backgroundColor() { - return p.backgroundColor(); +Color Window::backgroundColor() const { + return state.backgroundColor; +} + +bool Window::droppable() const { + return state.droppable; } Geometry Window::frameGeometry() { @@ -351,7 +361,7 @@ bool Window::focused() { return p.focused(); } -bool Window::fullScreen() { +bool Window::fullScreen() const { return state.fullScreen; } @@ -359,27 +369,43 @@ Geometry Window::geometry() { return p.geometry(); } -void Window::remove_(Layout& layout) { +string Window::menuFont() const { + return state.menuFont; +} + +bool Window::menuVisible() const { + return state.menuVisible; +} + +bool Window::modal() const { + return state.modal; +} + +void Window::remove(Layout& layout) { if(state.layout.remove(layout)) { p.remove(layout); - ((Sizable&)layout).state.window = nullptr; + layout.Sizable::state.window = nullptr; } } -void Window::remove_(Menu& menu) { +void Window::remove(Menu& menu) { if(state.menu.remove(menu)) { p.remove(menu); - ((Action&)menu).state.window = nullptr; + menu.Action::state.window = nullptr; } } -void Window::remove_(Widget& widget) { +void Window::remove(Widget& widget) { if(state.widget.remove(widget)) { p.remove(widget); - ((Sizable&)widget).state.window = nullptr; + widget.Sizable::state.window = nullptr; } } +bool Window::resizable() const { + return state.resizable; +} + void Window::setBackgroundColor(Color color) { state.backgroundColorOverride = true; state.backgroundColor = color; @@ -413,7 +439,7 @@ void Window::setGeometry(Geometry geometry) { return p.setGeometry(geometry); } -void Window::setMenuFont(string font) { +void Window::setMenuFont(const string& font) { state.menuFont = font; return p.setMenuFont(font); } @@ -433,20 +459,12 @@ void Window::setResizable(bool resizable) { return p.setResizable(resizable); } -void Window::setSmartGeometry(Geometry geometry) { - Geometry margin = p.frameMargin(); - return setGeometry({ - geometry.x + margin.x, geometry.y + margin.y, - geometry.width, geometry.height - }); -} - -void Window::setStatusFont(string font) { +void Window::setStatusFont(const string& font) { state.statusFont = font; return p.setStatusFont(font); } -void Window::setStatusText(string text) { +void Window::setStatusText(const string& text) { state.statusText = text; return p.setStatusText(text); } @@ -456,7 +474,7 @@ void Window::setStatusVisible(bool visible) { return p.setStatusVisible(visible); } -void Window::setTitle(string text) { +void Window::setTitle(const string& text) { state.title = text; return p.setTitle(text); } @@ -467,23 +485,47 @@ void Window::setVisible(bool visible) { return p.setVisible(visible); } -void Window::setWidgetFont(string font) { +void Window::setWidgetFont(const string& font) { state.widgetFont = font; return p.setWidgetFont(font); } -string Window::statusText() { +void Window::setWindowGeometry(Geometry geometry) { + Geometry margin = p.frameMargin(); + return setGeometry({ + geometry.x + margin.x, geometry.y + margin.y, + geometry.width, geometry.height + }); +} + +string Window::statusFont() const { + return state.statusFont; +} + +string Window::statusText() const { return state.statusText; } +bool Window::statusVisible() const { + return state.statusVisible; +} + void Window::synchronizeLayout() { if(visible() && applicationState.quit == false) setGeometry(geometry()); } -bool Window::visible() { +string Window::title() const { + return state.title; +} + +bool Window::visible() const { return state.visible; } +string Window::widgetFont() const { + return state.widgetFont; +} + Window::Window(): state(*new State), base_from_member(*new pWindow(*this)), @@ -500,7 +542,7 @@ Window::~Window() { //Action //====== -bool Action::enabled() { +bool Action::enabled() const { return state.enabled; } @@ -514,7 +556,7 @@ void Action::setVisible(bool visible) { return p.setVisible(visible); } -bool Action::visible() { +bool Action::visible() const { return state.visible; } @@ -542,6 +584,10 @@ void Menu::append(const group& list) { } } +image Menu::image() const { + return state.image; +} + void Menu::remove(const group& list) { for(auto& action : list) { if(state.action.remove(action)) { @@ -551,16 +597,20 @@ void Menu::remove(const group& list) { } } -void Menu::setImage(const image& image) { +void Menu::setImage(const nall::image& image) { state.image = image; return p.setImage(image); } -void Menu::setText(string text) { +void Menu::setText(const string& text) { state.text = text; return p.setText(text); } +string Menu::text() const { + return state.text; +} + Menu::Menu(): state(*new State), base_from_member(*new pMenu(*this)), @@ -591,16 +641,24 @@ Separator::~Separator() { //Item //==== -void Item::setImage(const image& image) { +image Item::image() const { + return state.image; +} + +void Item::setImage(const nall::image& image) { state.image = image; return p.setImage(image); } -void Item::setText(string text) { +void Item::setText(const string& text) { state.text = text; return p.setText(text); } +string Item::text() const { + return state.text; +} + Item::Item(): state(*new State), base_from_member(*new pItem(*this)), @@ -617,8 +675,8 @@ Item::~Item() { //CheckItem //========= -bool CheckItem::checked() { - return p.checked(); +bool CheckItem::checked() const { + return state.checked; } void CheckItem::setChecked(bool checked) { @@ -626,11 +684,15 @@ void CheckItem::setChecked(bool checked) { return p.setChecked(checked); } -void CheckItem::setText(string text) { +void CheckItem::setText(const string& text) { state.text = text; return p.setText(text); } +string CheckItem::text() const { + return state.text; +} + CheckItem::CheckItem(): state(*new State), base_from_member(*new pCheckItem(*this)), @@ -652,8 +714,8 @@ void RadioItem::group(const nall::group& list) { if(list.size()) list.first().setChecked(); } -bool RadioItem::checked() { - return p.checked(); +bool RadioItem::checked() const { + return state.checked; } void RadioItem::setChecked() { @@ -662,12 +724,12 @@ void RadioItem::setChecked() { return p.setChecked(); } -void RadioItem::setText(string text) { +void RadioItem::setText(const string& text) { state.text = text; return p.setText(text); } -string RadioItem::text() { +string RadioItem::text() const { return state.text; } @@ -690,12 +752,36 @@ RadioItem::~RadioItem() { //Sizable //======= -Layout* Sizable::layout() { - return state.layout; +bool Sizable::enabled() const { + return state.enabled; } -Window* Sizable::window() { - if(state.layout) return state.layout->window(); +bool Sizable::enabledToAll() const { + if(state.enabled == false) return false; + if(state.parent) return state.parent->enabledToAll(); + return true; +} + +Layout* Sizable::layout() const { + if(state.parent && dynamic_cast(state.parent)) return (Layout*)state.parent; + return nullptr; +} + +Sizable* Sizable::parent() const { + return state.parent; +} + +bool Sizable::visible() const { + return state.visible; +} + +bool Sizable::visibleToAll() const { + if(state.visible == false) return false; + if(state.parent) return state.parent->visibleToAll(); + return true; +} + +Window* Sizable::window() const { return state.window; } @@ -716,30 +802,33 @@ Sizable::~Sizable() { //====== void Layout::append(Sizable& sizable) { - sizable.state.layout = this; - sizable.state.window = nullptr; + sizable.state.parent = this; + sizable.state.window = Sizable::state.window; if(dynamic_cast(&sizable)) { - Layout &layout = (Layout&)sizable; + Layout& layout = (Layout&)sizable; layout.synchronizeLayout(); } if(dynamic_cast(&sizable)) { - Widget &widget = (Widget&)sizable; + Widget& widget = (Widget&)sizable; if(sizable.window()) sizable.window()->append(widget); } } void Layout::remove(Sizable& sizable) { if(dynamic_cast(&sizable)) { - Widget &widget = (Widget&)sizable; + Widget& widget = (Widget&)sizable; if(sizable.window()) sizable.window()->remove(widget); } - sizable.state.layout = nullptr; + sizable.state.parent = nullptr; sizable.state.window = nullptr; } +void Layout::reset() { +} + Layout::Layout(): state(*new State), base_from_member(*new pLayout(*this)), @@ -756,7 +845,7 @@ p(p) { Layout::~Layout() { if(layout()) layout()->remove(*this); - else if(window()) window()->remove(*this); + if(window()) window()->remove(*this); p.destructor(); delete &state; } @@ -764,19 +853,15 @@ Layout::~Layout() { //Widget //====== -bool Widget::enabled() { - return state.enabled; -} - bool Widget::focused() { return p.focused(); } -string Widget::font() { +string Widget::font() const { return state.font; } -Geometry Widget::geometry() { +Geometry Widget::geometry() const { return state.geometry; } @@ -785,7 +870,7 @@ Size Widget::minimumSize() { } void Widget::setEnabled(bool enabled) { - state.enabled = enabled; + Sizable::state.enabled = enabled; return p.setEnabled(enabled); } @@ -793,7 +878,7 @@ void Widget::setFocused() { return p.setFocused(); } -void Widget::setFont(string font) { +void Widget::setFont(const string& font) { state.font = font; return p.setFont(font); } @@ -804,12 +889,11 @@ void Widget::setGeometry(Geometry geometry) { } void Widget::setVisible(bool visible) { - state.visible = visible; + Sizable::state.visible = visible; return p.setVisible(visible); } -bool Widget::visible() { - return state.visible; +void Widget::synchronizeLayout() { } Widget::Widget(): @@ -837,17 +921,29 @@ Widget::~Widget() { //Button //====== -void Button::setImage(const image& image, Orientation orientation) { +image Button::image() const { + return state.image; +} + +Orientation Button::orientation() const { + return state.orientation; +} + +void Button::setImage(const nall::image& image, Orientation orientation) { state.image = image; state.orientation = orientation; return p.setImage(image, orientation); } -void Button::setText(string text) { +void Button::setText(const string& text) { state.text = text; return p.setText(text); } +nall::string Button::text() const { + return state.text; +} + Button::Button(): state(*new State), base_from_member(*new pButton(*this)), @@ -864,46 +960,95 @@ Button::~Button() { //Canvas //====== -uint32_t* Canvas::data() { +Color Canvas::color() const { + return state.color; +} + +uint32_t* Canvas::data() const { return state.data; } +bool Canvas::droppable() const { + return state.droppable; +} + +vector Canvas::gradient() const { + return state.gradient; +} + +image Canvas::image() const { + return state.image; +} + +Canvas::Mode Canvas::mode() const { + return state.mode; +} + +void Canvas::setColor(Color color) { + state.color = color; + return setMode(Canvas::Mode::Color); +} + +void Canvas::setData() { + if(state.width == 0 || state.height == 0) return; //dynamic sizing not supported in Mode::Data + return setMode(Canvas::Mode::Data); +} + void Canvas::setDroppable(bool droppable) { state.droppable = droppable; return p.setDroppable(droppable); } -bool Canvas::setImage(const nall::image& image) { - if(image.data == nullptr || image.width == 0 || image.height == 0) return false; - state.width = image.width; - state.height = image.height; - setSize({state.width, state.height}); - memcpy(state.data, image.data, state.width * state.height * sizeof(uint32_t)); - return true; +void Canvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) { + state.gradient[0] = topLeft; + state.gradient[1] = topRight; + state.gradient[2] = bottomLeft; + state.gradient[3] = bottomRight; + return setMode(Canvas::Mode::Gradient); +} + +void Canvas::setHorizontalGradient(Color left, Color right) { + state.gradient[0] = state.gradient[2] = left; + state.gradient[1] = state.gradient[3] = right; + return setMode(Canvas::Mode::Gradient); +} + +void Canvas::setImage(const nall::image& image) { + state.image = image; + return setMode(Canvas::Mode::Image); +} + +void Canvas::setMode(Mode mode) { + state.mode = mode; + return p.setMode(mode); } void Canvas::setSize(Size size) { + if(size.width == Size::Maximum) size.width = 0; + if(size.height == Size::Maximum) size.height = 0; state.width = size.width; state.height = size.height; delete[] state.data; - state.data = new uint32_t[size.width * size.height]; - return p.setSize(size); + state.data = new uint32_t[state.width * state.height](); + return setMode(state.mode); } -Size Canvas::size() { +void Canvas::setVerticalGradient(Color top, Color bottom) { + state.gradient[0] = state.gradient[1] = top; + state.gradient[2] = state.gradient[3] = bottom; + return setMode(Canvas::Mode::Gradient); +} + +Size Canvas::size() const { return {state.width, state.height}; } -void Canvas::update() { - return p.update(); -} - Canvas::Canvas(): state(*new State), base_from_member(*new pCanvas(*this)), Widget(base_from_member::value), p(base_from_member::value) { - state.data = new uint32_t[state.width * state.height]; + state.data = new uint32_t[state.width * state.height](); p.constructor(); } @@ -916,8 +1061,12 @@ Canvas::~Canvas() { //CheckButton //=========== -bool CheckButton::checked() { - return p.checked(); +bool CheckButton::checked() const { + return state.checked; +} + +image CheckButton::image() const { + return state.image; } void CheckButton::setChecked(bool checked) { @@ -925,11 +1074,21 @@ void CheckButton::setChecked(bool checked) { return p.setChecked(checked); } -void CheckButton::setText(string text) { +void CheckButton::setImage(const nall::image& image, Orientation orientation) { + state.image = image; + state.orientation = orientation; + return p.setImage(image, orientation); +} + +void CheckButton::setText(const string& text) { state.text = text; return p.setText(text); } +string CheckButton::text() const { + return state.text; +} + CheckButton::CheckButton(): state(*new State), base_from_member(*new pCheckButton(*this)), @@ -943,24 +1102,53 @@ CheckButton::~CheckButton() { delete &state; } +//CheckLabel +//========== + +bool CheckLabel::checked() const { + return state.checked; +} + +void CheckLabel::setChecked(bool checked) { + state.checked = checked; + return p.setChecked(checked); +} + +void CheckLabel::setText(const string& text) { + state.text = text; + return p.setText(text); +} + +string CheckLabel::text() const { + return state.text; +} + +CheckLabel::CheckLabel(): +state(*new State), +base_from_member(*new pCheckLabel(*this)), +Widget(base_from_member::value), +p(base_from_member::value) { + p.constructor(); +} + +CheckLabel::~CheckLabel() { + p.destructor(); + delete &state; +} + + //ComboButton //=========== -void ComboButton::append_(const lstring& list) { - for(auto& text : list) { - state.text.append(text); - p.append(text); - } +void ComboButton::append(const string& text) { + state.text.append(text); + return p.append(text); } -void ComboButton::modify(unsigned row, string text) { - state.text(row) = text; - p.modify(row, text); -} - -void ComboButton::remove(unsigned row) { - state.text.remove(row); - p.remove(row); +void ComboButton::remove(unsigned selection) { + if(selection >= state.text.size()) return; + state.text.remove(selection); + p.remove(selection); } void ComboButton::reset() { @@ -969,21 +1157,34 @@ void ComboButton::reset() { return p.reset(); } -unsigned ComboButton::selection() { - return p.selection(); +unsigned ComboButton::rows() const { + return state.text.size(); } -void ComboButton::setSelection(unsigned row) { - state.selection = row; - return p.setSelection(row); +unsigned ComboButton::selection() const { + return state.selection; } -string ComboButton::text() { - return state.text(selection()); +void ComboButton::setSelection(unsigned selection) { + if(selection >= state.text.size()) return; + state.selection = selection; + return p.setSelection(selection); } -string ComboButton::text(unsigned row) { - return state.text(row); +void ComboButton::setText(unsigned selection, const string& text) { + if(selection >= state.text.size()) return; + state.text[selection] = text; + p.setText(selection, text); +} + +string ComboButton::text() const { + if(state.text.empty()) return ""; + return state.text[state.selection]; +} + +string ComboButton::text(unsigned selection) const { + if(selection >= state.text.size()) return ""; + return state.text[selection]; } ComboButton::ComboButton(): @@ -1002,7 +1203,7 @@ ComboButton::~ComboButton() { //Console //======= -void Console::print_(string text) { +void Console::print(const string& text) { return p.print(text); } @@ -1023,9 +1224,63 @@ Console::~Console() { delete &state; } +//Frame +//===== + +void Frame::setLayout(Layout& layout) { + state.layout = &layout; + synchronizeLayout(); +} + +void Frame::setText(const string& text) { + state.text = text; + return p.setText(text); +} + +void Frame::synchronizeLayout() { + if(state.layout == nullptr) return; + state.layout->Sizable::state.window = Sizable::state.window; + state.layout->Sizable::state.parent = this; + state.layout->state.widget = this; + state.layout->synchronizeLayout(); +} + +string Frame::text() const { + return state.text; +} + +Frame::Frame(): +state(*new State), +base_from_member(*new pFrame(*this)), +Widget(base_from_member::value), +p(base_from_member::value) { + p.constructor(); +} + +Frame::~Frame() { + p.destructor(); + delete &state; +} + //HexEdit //======= +unsigned HexEdit::columns() const { + return state.columns; +} + +unsigned HexEdit::length() const { + return state.length; +} + +unsigned HexEdit::offset() const { + return state.offset; +} + +unsigned HexEdit::rows() const { + return state.rows; +} + void HexEdit::setColumns(unsigned columns) { state.columns = columns; return p.setColumns(columns); @@ -1066,12 +1321,12 @@ HexEdit::~HexEdit() { //HorizontalScroller //================== -unsigned HorizontalScroller::length() { +unsigned HorizontalScroller::length() const { return state.length; } -unsigned HorizontalScroller::position() { - return p.position(); +unsigned HorizontalScroller::position() const { + return state.position; } void HorizontalScroller::setLength(unsigned length) { @@ -1100,12 +1355,12 @@ HorizontalScroller::~HorizontalScroller() { //HorizontalSlider //================ -unsigned HorizontalSlider::length() { +unsigned HorizontalSlider::length() const { return state.length; } -unsigned HorizontalSlider::position() { - return p.position(); +unsigned HorizontalSlider::position() const { + return state.position; } void HorizontalSlider::setLength(unsigned length) { @@ -1134,11 +1389,15 @@ HorizontalSlider::~HorizontalSlider() { //Label //===== -void Label::setText(string text) { +void Label::setText(const string& text) { state.text = text; return p.setText(text); } +string Label::text() const { + return state.text; +} + Label::Label(): state(*new State), base_from_member(*new pLabel(*this)), @@ -1155,12 +1414,16 @@ Label::~Label() { //LineEdit //======== +bool LineEdit::editable() const { + return state.editable; +} + void LineEdit::setEditable(bool editable) { state.editable = editable; return p.setEditable(editable); } -void LineEdit::setText(string text) { +void LineEdit::setText(const string& text) { state.text = text; return p.setText(text); } @@ -1185,8 +1448,9 @@ LineEdit::~LineEdit() { //ListView //======== -void ListView::append_(const lstring& text) { +void ListView::append(const lstring& text) { state.checked.append(false); + state.image.append({}); state.text.append(text); return p.append(text); } @@ -1195,19 +1459,34 @@ void ListView::autoSizeColumns() { return p.autoSizeColumns(); } -bool ListView::checked(unsigned row) { - return p.checked(row); +bool ListView::checkable() const { + return state.checkable; } -void ListView::modify_(unsigned row, const lstring& text) { - state.text[row] = text; - return p.modify(row, text); +bool ListView::checked(unsigned selection) const { + if(selection >= state.text.size()) return false; + return state.checked[selection]; } -void ListView::remove(unsigned row) { - state.text.remove(row); - state.image.remove(row); - return p.remove(row); +unsigned ListView::columns() const { + return max(1u, state.headerText.size()); +} + +bool ListView::headerVisible() const { + return state.headerVisible; +} + +image ListView::image(unsigned selection, unsigned position) const { + if(selection >= state.text.size()) return {}; + return state.image[selection](position); +} + +void ListView::remove(unsigned selection) { + if(selection >= state.text.size()) return; + state.checked.remove(selection); + state.image.remove(selection); + state.text.remove(selection); + return p.remove(selection); } void ListView::reset() { @@ -1217,12 +1496,16 @@ void ListView::reset() { return p.reset(); } -bool ListView::selected() { - return p.selected(); +unsigned ListView::rows() const { + return state.text.size(); } -unsigned ListView::selection() { - return p.selection(); +bool ListView::selected() const { + return state.selected; +} + +unsigned ListView::selection() const { + return state.selection; } void ListView::setCheckable(bool checkable) { @@ -1230,12 +1513,13 @@ void ListView::setCheckable(bool checkable) { return p.setCheckable(checkable); } -void ListView::setChecked(unsigned row, bool checked) { - state.checked[row] = checked; - return p.setChecked(row, checked); +void ListView::setChecked(unsigned selection, bool checked) { + if(selection >= state.text.size()) return; + state.checked[selection] = checked; + return p.setChecked(selection, checked); } -void ListView::setHeaderText_(const lstring& text) { +void ListView::setHeaderText(const lstring& text) { state.headerText = text; return p.setHeaderText(text); } @@ -1245,9 +1529,10 @@ void ListView::setHeaderVisible(bool visible) { return p.setHeaderVisible(visible); } -void ListView::setImage(unsigned row, unsigned column, const nall::image& image) { - state.image(row)(column) = image; - return p.setImage(row, column, image); +void ListView::setImage(unsigned selection, unsigned position, const nall::image& image) { + if(selection >= state.text.size()) return; + state.image[selection](position) = image; + return p.setImage(selection, position, image); } void ListView::setSelected(bool selected) { @@ -1255,10 +1540,29 @@ void ListView::setSelected(bool selected) { return p.setSelected(selected); } -void ListView::setSelection(unsigned row) { +void ListView::setSelection(unsigned selection) { + if(selection >= state.text.size()) return; state.selected = true; - state.selection = row; - return p.setSelection(row); + state.selection = selection; + return p.setSelection(selection); +} + +void ListView::setText(unsigned selection, const lstring& text) { + if(selection >= state.text.size()) return; + for(unsigned position = 0; position < text.size(); position++) { + setText(selection, position, text[position]); + } +} + +void ListView::setText(unsigned selection, unsigned position, const string& text) { + if(selection >= state.text.size()) return; + state.text[selection](position) = text; + return p.setText(selection, position, text); +} + +string ListView::text(unsigned selection, unsigned position) const { + if(selection >= state.text.size()) return ""; + return state.text[selection](position); } ListView::ListView(): @@ -1277,6 +1581,10 @@ ListView::~ListView() { //ProgressBar //=========== +unsigned ProgressBar::position() const { + return state.position; +} + void ProgressBar::setPosition(unsigned position) { state.position = position; return p.setPosition(position); @@ -1303,21 +1611,35 @@ void RadioButton::group(const nall::group& list) { if(list.size()) list.first().setChecked(); } -bool RadioButton::checked() { - return p.checked(); +bool RadioButton::checked() const { + return state.checked; +} + +image RadioButton::image() const { + return state.image; } void RadioButton::setChecked() { - for(auto &item : state.group) item.state.checked = false; + for(auto& item : state.group) item.state.checked = false; state.checked = true; return p.setChecked(); } -void RadioButton::setText(string text) { +void RadioButton::setImage(const nall::image& image, Orientation orientation) { + state.image = image; + state.orientation = orientation; + return p.setImage(image, orientation); +} + +void RadioButton::setText(const string& text) { state.text = text; return p.setText(text); } +string RadioButton::text() const { + return state.text; +} + RadioButton::RadioButton(): state(*new State), base_from_member(*new pRadioButton(*this)), @@ -1334,9 +1656,140 @@ RadioButton::~RadioButton() { delete &state; } +//RadioLabel +//========== + +void RadioLabel::group(const nall::group& list) { + for(auto& item : list) item.p.setGroup(item.state.group = list); + if(list.size()) list.first().setChecked(); +} + +bool RadioLabel::checked() const { + return state.checked; +} + +void RadioLabel::setChecked() { + for(auto &item : state.group) item.state.checked = false; + state.checked = true; + return p.setChecked(); +} + +void RadioLabel::setText(const string& text) { + state.text = text; + return p.setText(text); +} + +string RadioLabel::text() const { + return state.text; +} + +RadioLabel::RadioLabel(): +state(*new State), +base_from_member(*new pRadioLabel(*this)), +Widget(base_from_member::value), +p(base_from_member::value) { + p.constructor(); +} + +RadioLabel::~RadioLabel() { + for(auto& item : state.group) { + if(&item != this) item.state.group.remove(*this); + } + p.destructor(); + delete &state; +} + +//TabFrame +//======== + +void TabFrame::append(const string& text, const nall::image& image) { + state.image.append(image); + state.layout.append(nullptr); + state.text.append(text); + return p.append(text, image); +} + +image TabFrame::image(unsigned selection) const { + if(selection >= state.text.size()) return {}; + return state.image[selection]; +} + +void TabFrame::remove(unsigned selection) { + if(selection >= state.text.size()) return; + state.image.remove(selection); + state.layout.remove(selection); + state.text.remove(selection); + return p.remove(selection); +} + +unsigned TabFrame::selection() const { + return state.selection; +} + +void TabFrame::setImage(unsigned selection, const nall::image& image) { + if(selection >= state.text.size()) return; + state.image[selection] = image; + return p.setImage(selection, image); +} + +void TabFrame::setLayout(unsigned selection, Layout& layout) { + if(selection >= state.text.size()) return; + state.layout[selection] = &layout; + synchronizeLayout(); +} + +void TabFrame::setSelection(unsigned selection) { + state.selection = selection; + return p.setSelection(selection); +} + +void TabFrame::setText(unsigned selection, const string& text) { + if(selection >= state.text.size()) return; + state.text[selection] = text; + return p.setText(selection, text); +} + +void TabFrame::synchronizeLayout() { + for(unsigned n = 0; n < state.layout.size(); n++) { + Layout* layout = state.layout[n]; + if(layout == nullptr) continue; + layout->Sizable::state.parent = this; + layout->Sizable::state.window = Sizable::state.window; + layout->state.widget = this; + layout->state.widgetSelection = n; + layout->synchronizeLayout(); + } +} + +unsigned TabFrame::tabs() const { + return state.text.size(); +} + +string TabFrame::text(unsigned selection) const { + if(selection >= state.text.size()) return ""; + return state.text[selection]; +} + +TabFrame::TabFrame(): +state(*new State), +base_from_member(*new pTabFrame(*this)), +Widget(base_from_member::value), +p(base_from_member::value) { + p.constructor(); +} + +TabFrame::~TabFrame() { + p.destructor(); + delete &state; +} + //TextEdit //======== +bool TextEdit::editable() const { + return state.editable; +} + void TextEdit::setCursorPosition(unsigned position) { state.cursorPosition = position; return p.setCursorPosition(position); @@ -1347,7 +1800,7 @@ void TextEdit::setEditable(bool editable) { return p.setEditable(editable); } -void TextEdit::setText(string text) { +void TextEdit::setText(const string& text) { state.text = text; return p.setText(text); } @@ -1361,7 +1814,7 @@ string TextEdit::text() { return p.text(); } -bool TextEdit::wordWrap() { +bool TextEdit::wordWrap() const { return state.wordWrap; } @@ -1381,12 +1834,12 @@ TextEdit::~TextEdit() { //VerticalScroller //================ -unsigned VerticalScroller::length() { +unsigned VerticalScroller::length() const { return state.length; } -unsigned VerticalScroller::position() { - return p.position(); +unsigned VerticalScroller::position() const { + return state.position; } void VerticalScroller::setLength(unsigned length) { @@ -1415,12 +1868,12 @@ VerticalScroller::~VerticalScroller() { //VerticalSlider //============== -unsigned VerticalSlider::length() { +unsigned VerticalSlider::length() const { return state.length; } -unsigned VerticalSlider::position() { - return p.position(); +unsigned VerticalSlider::position() const { + return state.position; } void VerticalSlider::setLength(unsigned length) { @@ -1449,6 +1902,10 @@ VerticalSlider::~VerticalSlider() { //Viewport //======== +bool Viewport::droppable() const { + return state.droppable; +} + uintptr_t Viewport::handle() { return p.handle(); } diff --git a/phoenix/core/core.hpp b/phoenix/core/core.hpp index b23b0d23..caef0bed 100644 --- a/phoenix/core/core.hpp +++ b/phoenix/core/core.hpp @@ -37,8 +37,10 @@ struct pWidget; struct pButton; struct pCanvas; struct pCheckButton; +struct pCheckLabel; struct pComboButton; struct pConsole; +struct pFrame; struct pHexEdit; struct pHorizontalScroller; struct pHorizontalSlider; @@ -47,6 +49,8 @@ struct pLineEdit; struct pListView; struct pProgressBar; struct pRadioButton; +struct pRadioLabel; +struct pTabFrame; struct pTextEdit; struct pVerticalScroller; struct pVerticalSlider; @@ -59,7 +63,7 @@ struct Application { static bool pendingEvents(); static void processEvents(); static void quit(); - static void setName(nall::string name); + static void setName(const nall::string& name); Application() = delete; struct State; @@ -78,17 +82,13 @@ struct Application { }; }; -enum : unsigned { - MaximumSize = ~0u, - MinimumSize = 0u, -}; - struct Color { uint8_t red, green, blue, alpha; uint32_t rgb() const; - uint32_t rgba() const; - inline Color() : red(0), green(0), blue(0), alpha(255) {} - inline Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255) : red(red), green(green), blue(blue), alpha(alpha) {} + uint32_t argb() const; + inline Color() : alpha(255), red(0), green(0), blue(0) {} + inline Color(uint8_t red, uint8_t green, uint8_t blue) : alpha(255), red(red), green(green), blue(blue) {} + inline Color(uint8_t alpha, uint8_t red, uint8_t green, uint8_t blue) : alpha(alpha), red(red), green(green), blue(blue) {} }; struct Position { @@ -98,6 +98,9 @@ struct Position { }; struct Size { + static const unsigned Maximum = ~0u; + static const unsigned Minimum = 0u; + unsigned width, height; inline Size() : width(0), height(0) {} template inline Size(W width, H height) : width(width), height(height) {} @@ -112,16 +115,16 @@ struct Geometry { inline Geometry() : x(0), y(0), width(0), height(0) {} inline Geometry(Position position, Size size) : x(position.x), y(position.y), width(size.width), height(size.height) {} template inline Geometry(X x, Y y, W width, H height) : x(x), y(y), width(width), height(height) {} - Geometry(nall::string text); + Geometry(const nall::string& text); }; enum class Orientation : unsigned { Horizontal, Vertical }; struct Font { - static nall::string serif(unsigned size = 0, nall::string style = ""); - static nall::string sans(unsigned size = 0, nall::string style = ""); - static nall::string monospace(unsigned size = 0, nall::string style = ""); - static Size size(nall::string font, nall::string text); + static nall::string serif(unsigned size = 0, const nall::string& style = ""); + static nall::string sans(unsigned size = 0, const nall::string& style = ""); + static nall::string monospace(unsigned size = 0, const nall::string& style = ""); + static Size size(const nall::string& font, const nall::string& text); Font() = delete; }; @@ -148,15 +151,13 @@ struct Mouse { }; struct BrowserWindow { - template BrowserWindow& setFilters(Args&&... args) { return setFilters_({args...}); } - nall::string directory(); nall::string open(); nall::string save(); - BrowserWindow& setFilters_(const nall::lstring& filters); + BrowserWindow& setFilters(const nall::lstring& filters); BrowserWindow& setParent(Window& parent); - BrowserWindow& setPath(nall::string path); - BrowserWindow& setTitle(nall::string title); + BrowserWindow& setPath(const nall::string& path); + BrowserWindow& setTitle(const nall::string& title); BrowserWindow(); ~BrowserWindow(); @@ -183,11 +184,11 @@ struct MessageWindow { Response information(Buttons = Buttons::Ok); Response question(Buttons = Buttons::YesNo); MessageWindow& setParent(Window& parent); - MessageWindow& setText(nall::string text); - MessageWindow& setTitle(nall::string title); + MessageWindow& setText(const nall::string& text); + MessageWindow& setTitle(const nall::string& title); Response warning(Buttons = Buttons::Ok); - MessageWindow(nall::string text = ""); + MessageWindow(const nall::string& text = ""); ~MessageWindow(); struct State; State& state; @@ -204,8 +205,10 @@ struct Object { struct Timer : private nall::base_from_member, Object { nall::function onActivate; + bool enabled() const; + unsigned interval() const; void setEnabled(bool enabled = true); - void setInterval(unsigned milliseconds); + void setInterval(unsigned interval); //in milliseconds Timer(); ~Timer(); @@ -222,45 +225,47 @@ struct Window : private nall::base_from_member, Object { nall::function onMove; nall::function onSize; - static Window& none(); - - inline void append() {} - inline void remove() {} - template void append(T& arg, Args&&... args) { append_(arg); append(args...); } - template void remove(T& arg, Args&&... args) { remove_(arg); remove(args...); } - - void append_(Layout& layout); - void append_(Menu& menu); - void append_(Widget& widget); - Color backgroundColor(); + void append(Layout& layout); + void append(Menu& menu); + void append(Widget& widget); + Color backgroundColor() const; + bool droppable() const; Geometry frameGeometry(); Geometry frameMargin(); bool focused(); - bool fullScreen(); + bool fullScreen() const; Geometry geometry(); - void remove_(Layout& layout); - void remove_(Menu& menu); - void remove_(Widget& widget); + nall::string menuFont() const; + bool menuVisible() const; + bool modal() const; + void remove(Layout& layout); + void remove(Menu& menu); + void remove(Widget& widget); + bool resizable() const; void setBackgroundColor(Color color); void setDroppable(bool droppable = true); void setFrameGeometry(Geometry geometry); void setFocused(); void setFullScreen(bool fullScreen = true); void setGeometry(Geometry geometry); - void setMenuFont(nall::string font); + void setMenuFont(const nall::string& font); void setMenuVisible(bool visible = true); void setModal(bool modal = true); void setResizable(bool resizable = true); - void setSmartGeometry(Geometry geometry); - void setStatusFont(nall::string font); - void setStatusText(nall::string text); + void setStatusFont(const nall::string& font); + void setStatusText(const nall::string& text); void setStatusVisible(bool visible = true); - void setTitle(nall::string text); + void setTitle(const nall::string& text); void setVisible(bool visible = true); - void setWidgetFont(nall::string font); - nall::string statusText(); + void setWidgetFont(const nall::string& font); + void setWindowGeometry(Geometry geometry); + nall::string statusFont() const; + nall::string statusText() const; + bool statusVisible() const; void synchronizeLayout(); - bool visible(); + nall::string title() const; + bool visible() const; + nall::string widgetFont() const; Window(); ~Window(); @@ -270,10 +275,10 @@ struct Window : private nall::base_from_member, Object { }; struct Action : Object { - bool enabled(); + bool enabled() const; void setEnabled(bool enabled = true); void setVisible(bool visible = true); - bool visible(); + bool visible() const; Action(pAction& p); ~Action(); @@ -283,13 +288,12 @@ struct Action : Object { }; struct Menu : private nall::base_from_member, Action { - template void append(Args&&... args) { append({std::forward(args)...}); } - template void remove(Args&&... args) { remove({std::forward(args)...}); } - void append(const nall::group& list); + nall::image image() const; void remove(const nall::group& list); void setImage(const nall::image& image = nall::image{}); - void setText(nall::string text); + void setText(const nall::string& text); + nall::string text() const; Menu(); ~Menu(); @@ -307,8 +311,10 @@ struct Separator : private nall::base_from_member, Action { struct Item : private nall::base_from_member, Action { nall::function onActivate; + nall::image image() const; void setImage(const nall::image& image = nall::image{}); - void setText(nall::string text); + void setText(const nall::string& text); + nall::string text() const; Item(); ~Item(); @@ -320,9 +326,10 @@ struct Item : private nall::base_from_member, Action { struct CheckItem : private nall::base_from_member, Action { nall::function onToggle; - bool checked(); + bool checked() const; void setChecked(bool checked = true); - void setText(nall::string text); + void setText(const nall::string& text); + nall::string text() const; CheckItem(); ~CheckItem(); @@ -337,10 +344,10 @@ struct RadioItem : private nall::base_from_member, Action { nall::function onActivate; - bool checked(); + bool checked() const; void setChecked(); - void setText(nall::string text); - nall::string text(); + void setText(const nall::string& text); + nall::string text() const; RadioItem(); ~RadioItem(); @@ -350,14 +357,18 @@ struct RadioItem : private nall::base_from_member, Action { }; struct Sizable : Object { - virtual bool enabled() = 0; - Layout* layout(); + bool enabled() const; + bool enabledToAll() const; + Layout* layout() const; virtual Size minimumSize() = 0; + Sizable* parent() const; virtual void setEnabled(bool enabled = true) = 0; virtual void setGeometry(Geometry geometry) = 0; virtual void setVisible(bool visible = true) = 0; - virtual bool visible() = 0; - Window* window(); + virtual void synchronizeLayout() = 0; + bool visible() const; + bool visibleToAll() const; + Window* window() const; Sizable(pSizable& p); ~Sizable(); @@ -369,8 +380,7 @@ struct Sizable : Object { struct Layout : private nall::base_from_member, Sizable { virtual void append(Sizable& sizable); virtual void remove(Sizable& sizable); - virtual void reset() {} - virtual void synchronizeLayout() = 0; + virtual void reset(); Layout(); Layout(pLayout& p); @@ -381,17 +391,18 @@ struct Layout : private nall::base_from_member, Sizable { }; struct Widget : private nall::base_from_member, Sizable { - bool enabled(); + nall::function onSize; + bool focused(); - nall::string font(); - Geometry geometry(); + nall::string font() const; + Geometry geometry() const; Size minimumSize(); void setEnabled(bool enabled = true); void setFocused(); - void setFont(nall::string font); + void setFont(const nall::string& font); void setGeometry(Geometry geometry); void setVisible(bool visible = true); - bool visible(); + void synchronizeLayout(); Widget(); Widget(pWidget& p); @@ -404,8 +415,11 @@ struct Widget : private nall::base_from_member, Sizable { struct Button : private nall::base_from_member, Widget { nall::function onActivate; - void setImage(const nall::image& image = nall::image{}, Orientation = Orientation::Horizontal); - void setText(nall::string text); + nall::image image() const; + Orientation orientation() const; + void setImage(const nall::image& image = nall::image{}, Orientation orientation = Orientation::Horizontal); + void setText(const nall::string& text); + nall::string text() const; Button(); ~Button(); @@ -415,18 +429,30 @@ struct Button : private nall::base_from_member, Widget { }; struct Canvas : private nall::base_from_member, Widget { + enum class Mode : unsigned { Color, Gradient, Image, Data }; + nall::function onDrop; nall::function onMouseLeave; nall::function onMouseMove; nall::function onMousePress; nall::function onMouseRelease; - uint32_t* data(); + Color color() const; + uint32_t* data() const; + bool droppable() const; + nall::vector gradient() const; + nall::image image() const; + Mode mode() const; + void setColor(Color color); + void setData(); void setDroppable(bool droppable = true); - bool setImage(const nall::image& image); + void setHorizontalGradient(Color left, Color right); + void setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight); + void setImage(const nall::image& image); + void setMode(Mode mode); void setSize(Size size); - Size size(); - void update(); + void setVerticalGradient(Color top, Color bottom); + Size size() const; Canvas(); ~Canvas(); @@ -438,9 +464,12 @@ struct Canvas : private nall::base_from_member, Widget { struct CheckButton : private nall::base_from_member, Widget { nall::function onToggle; - bool checked(); + bool checked() const; + nall::image image() const; void setChecked(bool checked = true); - void setText(nall::string text); + void setImage(const nall::image& image = nall::image{}, Orientation orientation = Orientation::Horizontal); + void setText(const nall::string& text); + nall::string text() const; CheckButton(); ~CheckButton(); @@ -449,19 +478,33 @@ struct CheckButton : private nall::base_from_member, Widget { pCheckButton& p; }; +struct CheckLabel : private nall::base_from_member, Widget { + nall::function onToggle; + + bool checked() const; + void setChecked(bool checked = true); + void setText(const nall::string& text); + nall::string text() const; + + CheckLabel(); + ~CheckLabel(); + struct State; + State& state; + pCheckLabel& p; +}; + struct ComboButton : private nall::base_from_member, Widget { nall::function onChange; - template void append(Args&&... args) { append_({args...}); } - - void append_(const nall::lstring& list); - void modify(unsigned row, nall::string text); - void remove(unsigned row); + void append(const nall::string& text = ""); + void remove(unsigned selection); void reset(); - unsigned selection(); - void setSelection(unsigned row); - nall::string text(); - nall::string text(unsigned row); + unsigned rows() const; + unsigned selection() const; + void setSelection(unsigned selection); + void setText(unsigned selection, const nall::string& text); + nall::string text() const; + nall::string text(unsigned selection) const; ComboButton(); ~ComboButton(); @@ -473,9 +516,9 @@ struct ComboButton : private nall::base_from_member, Widget { struct Console : private nall::base_from_member, Widget { nall::function onActivate; - template void print(Args&&... args) { print_({args...}); } + template void print(Args&&... args) { print({args...}); } - void print_(nall::string text); + void print(const nall::string& text); void reset(); Console(); @@ -485,10 +528,27 @@ struct Console : private nall::base_from_member, Widget { pConsole& p; }; +struct Frame : private nall::base_from_member, Widget { + void setLayout(Layout& layout); + void setText(const nall::string& text); + void synchronizeLayout(); + nall::string text() const; + + Frame(); + ~Frame(); + struct State; + State& state; + pFrame& p; +}; + struct HexEdit : private nall::base_from_member, Widget { nall::function onRead; nall::function onWrite; + unsigned columns() const; + unsigned length() const; + unsigned offset() const; + unsigned rows() const; void setColumns(unsigned columns); void setLength(unsigned length); void setOffset(unsigned offset); @@ -505,8 +565,8 @@ struct HexEdit : private nall::base_from_member, Widget { struct HorizontalScroller : private nall::base_from_member, Widget { nall::function onChange; - unsigned length(); - unsigned position(); + unsigned length() const; + unsigned position() const; void setLength(unsigned length); void setPosition(unsigned position); @@ -520,8 +580,8 @@ struct HorizontalScroller : private nall::base_from_member struct HorizontalSlider : private nall::base_from_member, Widget { nall::function onChange; - unsigned length(); - unsigned position(); + unsigned length() const; + unsigned position() const; void setLength(unsigned length); void setPosition(unsigned position); @@ -533,7 +593,8 @@ struct HorizontalSlider : private nall::base_from_member, Wi }; struct Label : private nall::base_from_member, Widget { - void setText(nall::string text); + void setText(const nall::string& text); + nall::string text() const; Label(); ~Label(); @@ -546,8 +607,9 @@ struct LineEdit : private nall::base_from_member, Widget { nall::function onActivate; nall::function onChange; + bool editable() const; void setEditable(bool editable = true); - void setText(nall::string text); + void setText(const nall::string& text); nall::string text(); LineEdit(); @@ -562,25 +624,28 @@ struct ListView : private nall::base_from_member, Widget { nall::function onChange; nall::function onToggle; - template void append(Args&&... args) { append_({args...}); } - template void modify(unsigned row, Args&&... args) { modify_(row, {args...}); } - template void setHeaderText(Args&&... args) { setHeaderText_({args...}); } - - void append_(const nall::lstring& list); + void append(const nall::lstring& text); void autoSizeColumns(); - bool checked(unsigned row); - void modify_(unsigned row, const nall::lstring& list); - void remove(unsigned row); + bool checkable() const; + bool checked(unsigned selection) const; + unsigned columns() const; + bool headerVisible() const; + nall::image image(unsigned selection, unsigned position) const; + void remove(unsigned selection); void reset(); - bool selected(); - unsigned selection(); + unsigned rows() const; + bool selected() const; + unsigned selection() const; void setCheckable(bool checkable = true); - void setChecked(unsigned row, bool checked = true); - void setHeaderText_(const nall::lstring& list); + void setChecked(unsigned selection, bool checked = true); + void setHeaderText(const nall::lstring& text); void setHeaderVisible(bool visible = true); - void setImage(unsigned row, unsigned column, const nall::image& image = nall::image{}); + void setImage(unsigned selection, unsigned position, const nall::image& image = nall::image{}); void setSelected(bool selected = true); - void setSelection(unsigned row); + void setSelection(unsigned selection); + void setText(unsigned selection, const nall::lstring& text); + void setText(unsigned selection, unsigned position, const nall::string& text); + nall::string text(unsigned selection, unsigned position) const; ListView(); ~ListView(); @@ -590,6 +655,7 @@ struct ListView : private nall::base_from_member, Widget { }; struct ProgressBar : private nall::base_from_member, Widget { + unsigned position() const; void setPosition(unsigned position); ProgressBar(); @@ -605,9 +671,12 @@ struct RadioButton : private nall::base_from_member, Widget { nall::function onActivate; - bool checked(); + bool checked() const; + nall::image image() const; void setChecked(); - void setText(nall::string text); + void setImage(const nall::image& image = nall::image{}, Orientation orientation = Orientation::Horizontal); + void setText(const nall::string& text); + nall::string text() const; RadioButton(); ~RadioButton(); @@ -616,15 +685,56 @@ struct RadioButton : private nall::base_from_member, Widget { pRadioButton& p; }; +struct RadioLabel : private nall::base_from_member, Widget { + template static void group(Args&&... args) { group({std::forward(args)...}); } + static void group(const nall::group& list); + + nall::function onActivate; + + bool checked() const; + void setChecked(); + void setText(const nall::string& text); + nall::string text() const; + + RadioLabel(); + ~RadioLabel(); + struct State; + State& state; + pRadioLabel& p; +}; + +struct TabFrame : private nall::base_from_member, Widget { + nall::function onChange; + + void append(const nall::string& text = "", const nall::image& image = nall::image{}); + nall::image image(unsigned selection) const; + void remove(unsigned selection); + unsigned selection() const; + void setImage(unsigned selection, const nall::image& image = nall::image{}); + void setLayout(unsigned selection, Layout& layout); + void setSelection(unsigned selection); + void setText(unsigned selection, const nall::string& text); + void synchronizeLayout(); + unsigned tabs() const; + nall::string text(unsigned selection) const; + + TabFrame(); + ~TabFrame(); + struct State; + State& state; + pTabFrame& p; +}; + struct TextEdit : private nall::base_from_member, Widget { nall::function onChange; + bool editable() const; void setCursorPosition(unsigned position); void setEditable(bool editable = true); - void setText(nall::string text); + void setText(const nall::string& text); void setWordWrap(bool wordWrap = true); nall::string text(); - bool wordWrap(); + bool wordWrap() const; TextEdit(); ~TextEdit(); @@ -636,8 +746,8 @@ struct TextEdit : private nall::base_from_member, Widget { struct VerticalScroller : private nall::base_from_member, Widget { nall::function onChange; - unsigned length(); - unsigned position(); + unsigned length() const; + unsigned position() const; void setLength(unsigned length); void setPosition(unsigned position); @@ -651,8 +761,8 @@ struct VerticalScroller : private nall::base_from_member, Wi struct VerticalSlider : private nall::base_from_member, Widget { nall::function onChange; - unsigned length(); - unsigned position(); + unsigned length() const; + unsigned position() const; void setLength(unsigned length); void setPosition(unsigned position); @@ -670,6 +780,7 @@ struct Viewport : private nall::base_from_member, Widget { nall::function onMousePress; nall::function onMouseRelease; + bool droppable() const; uintptr_t handle(); void setDroppable(bool droppable = true); diff --git a/phoenix/core/layout/fixed-layout.cpp b/phoenix/core/layout/fixed-layout.cpp index 5eeb8c84..d29189e5 100644 --- a/phoenix/core/layout/fixed-layout.cpp +++ b/phoenix/core/layout/fixed-layout.cpp @@ -10,13 +10,8 @@ void FixedLayout::append(Sizable& sizable) { if(window()) window()->synchronizeLayout(); } -bool FixedLayout::enabled() { - if(layout()) return state.enabled && layout()->enabled(); - return state.enabled; -} - Size FixedLayout::minimumSize() { - unsigned width = MinimumSize, height = MinimumSize; + unsigned width = Size::Minimum, height = Size::Minimum; for(auto& child : children) { width = max(width, child.sizable->minimumSize().width); height = max(height, child.sizable->minimumSize().height); @@ -42,9 +37,9 @@ void FixedLayout::reset() { } void FixedLayout::setEnabled(bool enabled) { - state.enabled = enabled; + Sizable::state.enabled = enabled; for(auto& child : children) { - child.sizable->setVisible(dynamic_cast(child.sizable) ? child.sizable->enabled() : enabled); + child.sizable->setEnabled(child.sizable->enabled()); } } @@ -52,29 +47,22 @@ void FixedLayout::setGeometry(Geometry geometry) { } void FixedLayout::setVisible(bool visible) { - state.visible = visible; + Sizable::state.visible = visible; for(auto& child : children) { - child.sizable->setVisible(dynamic_cast(child.sizable) ? child.sizable->visible() : visible); + child.sizable->setVisible(child.sizable->visible()); } } void FixedLayout::synchronizeLayout() { for(auto& child : children) { Layout::append(*child.sizable); - child.sizable->setGeometry(child.geometry); + Geometry childGeometry = child.geometry; + if((signed)childGeometry.width < 1) childGeometry.width = 1; + if((signed)childGeometry.height < 1) childGeometry.height = 1; + child.sizable->setGeometry(childGeometry); } } -bool FixedLayout::visible() { - if(layout()) return state.visible && layout()->visible(); - return state.visible; -} - -FixedLayout::FixedLayout() { - state.enabled = true; - state.visible = true; -} - FixedLayout::~FixedLayout() { while(children.size()) remove(*children[0].sizable); } diff --git a/phoenix/core/layout/fixed-layout.hpp b/phoenix/core/layout/fixed-layout.hpp index 2b4cb630..881bee76 100644 --- a/phoenix/core/layout/fixed-layout.hpp +++ b/phoenix/core/layout/fixed-layout.hpp @@ -1,7 +1,6 @@ struct FixedLayout : Layout { void append(Sizable& sizable, Geometry geometry); void append(Sizable& sizable); - bool enabled(); Size minimumSize(); void remove(Sizable& sizable); void reset(); @@ -9,16 +8,9 @@ struct FixedLayout : Layout { void setGeometry(Geometry geometry); void setVisible(bool visible = true); void synchronizeLayout(); - bool visible(); - FixedLayout(); ~FixedLayout(); //private: - struct State { - bool enabled; - bool visible; - } state; - struct Children { Sizable* sizable; Geometry geometry; diff --git a/phoenix/core/layout/horizontal-layout.cpp b/phoenix/core/layout/horizontal-layout.cpp index e378c804..574d48e3 100644 --- a/phoenix/core/layout/horizontal-layout.cpp +++ b/phoenix/core/layout/horizontal-layout.cpp @@ -11,17 +11,12 @@ void HorizontalLayout::append(Sizable& sizable) { if(window()) window()->synchronizeLayout(); } -bool HorizontalLayout::enabled() { - if(layout()) return state.enabled && layout()->enabled(); - return state.enabled; -} - Size HorizontalLayout::minimumSize() { unsigned width = 0, height = 0; for(auto& child : children) { width += child.spacing; - if(child.width == MinimumSize || child.width == MaximumSize) { + if(child.width == Size::Minimum || child.width == Size::Maximum) { width += child.sizable->minimumSize().width; continue; } @@ -29,7 +24,7 @@ Size HorizontalLayout::minimumSize() { } for(auto& child : children) { - if(child.height == MinimumSize || child.height == MaximumSize) { + if(child.height == Size::Minimum || child.height == Size::Maximum) { height = max(height, child.sizable->minimumSize().height); continue; } @@ -43,7 +38,7 @@ void HorizontalLayout::remove(Sizable& sizable) { for(unsigned n = 0; n < children.size(); n++) { if(children[n].sizable == &sizable) { if(dynamic_cast(children[n].sizable)) { - Layout *layout = (Layout*)children[n].sizable; + Layout* layout = (Layout*)children[n].sizable; layout->reset(); } children.remove(n); @@ -66,17 +61,17 @@ void HorizontalLayout::setAlignment(double alignment) { } void HorizontalLayout::setEnabled(bool enabled) { - state.enabled = enabled; + Sizable::state.enabled = enabled; for(auto& child : children) { - child.sizable->setEnabled(dynamic_cast(child.sizable) ? child.sizable->enabled() : enabled); + child.sizable->setEnabled(child.sizable->enabled()); } } void HorizontalLayout::setGeometry(Geometry containerGeometry) { auto children = this->children; for(auto& child : children) { - if(child.width == MinimumSize) child.width = child.sizable->minimumSize().width; - if(child.height == MinimumSize) child.height = child.sizable->minimumSize().height; + if(child.width == Size::Minimum) child.width = child.sizable->minimumSize().width; + if(child.height == Size::Minimum) child.height = child.sizable->minimumSize().height; } Geometry geometry = containerGeometry; @@ -87,14 +82,14 @@ void HorizontalLayout::setGeometry(Geometry containerGeometry) { unsigned minimumWidth = 0, maximumWidthCounter = 0; for(auto& child : children) { - if(child.width == MaximumSize) maximumWidthCounter++; - if(child.width != MaximumSize) minimumWidth += child.width; + if(child.width == Size::Maximum) maximumWidthCounter++; + if(child.width != Size::Maximum) minimumWidth += child.width; minimumWidth += child.spacing; } for(auto& child : children) { - if(child.width == MaximumSize) child.width = (geometry.width - minimumWidth) / maximumWidthCounter; - if(child.height == MaximumSize) child.height = geometry.height; + if(child.width == Size::Maximum) child.width = (geometry.width - minimumWidth) / maximumWidthCounter; + if(child.height == Size::Maximum) child.height = geometry.height; } unsigned maximumHeight = 0; @@ -103,6 +98,8 @@ void HorizontalLayout::setGeometry(Geometry containerGeometry) { for(auto& child : children) { unsigned pivot = (maximumHeight - child.height) * state.alignment; Geometry childGeometry = {geometry.x, geometry.y + pivot, child.width, child.height}; + if((signed)childGeometry.width < 1) childGeometry.width = 1; + if((signed)childGeometry.height < 1) childGeometry.height = 1; child.sizable->setGeometry(childGeometry); geometry.x += child.width + child.spacing; @@ -115,9 +112,9 @@ void HorizontalLayout::setMargin(unsigned margin) { } void HorizontalLayout::setVisible(bool visible) { - state.visible = visible; + Sizable::state.visible = visible; for(auto& child : children) { - child.sizable->setVisible(dynamic_cast(child.sizable) ? child.sizable->visible() : visible); + child.sizable->setVisible(child.sizable->visible()); } } @@ -125,18 +122,6 @@ void HorizontalLayout::synchronizeLayout() { for(auto& child : children) Layout::append(*child.sizable); } -bool HorizontalLayout::visible() { - if(layout()) return state.visible && layout()->visible(); - return state.visible; -} - -HorizontalLayout::HorizontalLayout() { - state.alignment = 0.5; - state.enabled = true; - state.margin = 0; - state.visible = true; -} - HorizontalLayout::~HorizontalLayout() { while(children.size()) remove(*children[0].sizable); } diff --git a/phoenix/core/layout/horizontal-layout.hpp b/phoenix/core/layout/horizontal-layout.hpp index ca29b691..c12eda8b 100644 --- a/phoenix/core/layout/horizontal-layout.hpp +++ b/phoenix/core/layout/horizontal-layout.hpp @@ -1,7 +1,6 @@ struct HorizontalLayout : public Layout { void append(Sizable& sizable, Size size, unsigned spacing = 0); void append(Sizable& sizable); - bool enabled(); Size minimumSize(); void remove(Sizable& sizable); void reset(); @@ -11,16 +10,12 @@ struct HorizontalLayout : public Layout { void setMargin(unsigned margin); void setVisible(bool visible = true); void synchronizeLayout(); - bool visible(); - HorizontalLayout(); ~HorizontalLayout(); //private: struct State { - double alignment; - bool enabled; - unsigned margin; - bool visible; + double alignment = 0.5; + unsigned margin = 0; } state; struct Children { diff --git a/phoenix/core/layout/vertical-layout.cpp b/phoenix/core/layout/vertical-layout.cpp index 0e2451bd..804a26db 100644 --- a/phoenix/core/layout/vertical-layout.cpp +++ b/phoenix/core/layout/vertical-layout.cpp @@ -11,16 +11,11 @@ void VerticalLayout::append(Sizable& sizable) { if(window()) window()->synchronizeLayout(); } -bool VerticalLayout::enabled() { - if(layout()) return state.enabled && layout()->enabled(); - return state.enabled; -} - Size VerticalLayout::minimumSize() { unsigned width = 0, height = 0; for(auto& child : children) { - if(child.width == MinimumSize || child.width == MaximumSize) { + if(child.width == Size::Minimum || child.width == Size::Maximum) { width = max(width, child.sizable->minimumSize().width); continue; } @@ -29,7 +24,7 @@ Size VerticalLayout::minimumSize() { for(auto& child : children) { height += child.spacing; - if(child.height == MinimumSize || child.height == MaximumSize) { + if(child.height == Size::Minimum || child.height == Size::Maximum) { height += child.sizable->minimumSize().height; continue; } @@ -43,7 +38,7 @@ void VerticalLayout::remove(Sizable& sizable) { for(unsigned n = 0; n < children.size(); n++) { if(children[n].sizable == &sizable) { if(dynamic_cast(children[n].sizable)) { - Layout *layout = (Layout*)children[n].sizable; + Layout* layout = (Layout*)children[n].sizable; layout->reset(); } children.remove(n); @@ -66,17 +61,17 @@ void VerticalLayout::setAlignment(double alignment) { } void VerticalLayout::setEnabled(bool enabled) { - state.enabled = enabled; + Sizable::state.enabled = enabled; for(auto& child : children) { - child.sizable->setEnabled(dynamic_cast(child.sizable) ? child.sizable->enabled() : enabled); + child.sizable->setEnabled(child.sizable->enabled()); } } void VerticalLayout::setGeometry(Geometry containerGeometry) { auto children = this->children; for(auto& child : children) { - if(child.width == MinimumSize) child.width = child.sizable->minimumSize().width; - if(child.height == MinimumSize) child.height = child.sizable->minimumSize().height; + if(child.width == Size::Minimum) child.width = child.sizable->minimumSize().width; + if(child.height == Size::Minimum) child.height = child.sizable->minimumSize().height; } Geometry geometry = containerGeometry; @@ -87,14 +82,14 @@ void VerticalLayout::setGeometry(Geometry containerGeometry) { unsigned minimumHeight = 0, maximumHeightCounter = 0; for(auto& child : children) { - if(child.height == MaximumSize) maximumHeightCounter++; - if(child.height != MaximumSize) minimumHeight += child.height; + if(child.height == Size::Maximum) maximumHeightCounter++; + if(child.height != Size::Maximum) minimumHeight += child.height; minimumHeight += child.spacing; } for(auto& child : children) { - if(child.width == MaximumSize) child.width = geometry.width; - if(child.height == MaximumSize) child.height = (geometry.height - minimumHeight) / maximumHeightCounter; + if(child.width == Size::Maximum) child.width = geometry.width; + if(child.height == Size::Maximum) child.height = (geometry.height - minimumHeight) / maximumHeightCounter; } unsigned maximumWidth = 0; @@ -103,6 +98,8 @@ void VerticalLayout::setGeometry(Geometry containerGeometry) { for(auto& child : children) { unsigned pivot = (maximumWidth - child.width) * state.alignment; Geometry childGeometry = {geometry.x + pivot, geometry.y, child.width, child.height}; + if((signed)childGeometry.width < 1) childGeometry.width = 1; + if((signed)childGeometry.height < 1) childGeometry.height = 1; child.sizable->setGeometry(childGeometry); geometry.y += child.height + child.spacing; @@ -115,9 +112,9 @@ void VerticalLayout::setMargin(unsigned margin) { } void VerticalLayout::setVisible(bool visible) { - state.visible = visible; + Sizable::state.visible = visible; for(auto& child : children) { - child.sizable->setVisible(dynamic_cast(child.sizable) ? child.sizable->visible() : visible); + child.sizable->setVisible(child.sizable->visible()); } } @@ -125,18 +122,6 @@ void VerticalLayout::synchronizeLayout() { for(auto& child : children) Layout::append(*child.sizable); } -bool VerticalLayout::visible() { - if(layout()) return state.visible && layout()->visible(); - return state.visible; -} - -VerticalLayout::VerticalLayout() { - state.alignment = 0.0; - state.enabled = true; - state.margin = 0; - state.visible = true; -} - VerticalLayout::~VerticalLayout() { while(children.size()) remove(*children[0].sizable); } diff --git a/phoenix/core/layout/vertical-layout.hpp b/phoenix/core/layout/vertical-layout.hpp index 7e8c7809..db96d6e7 100644 --- a/phoenix/core/layout/vertical-layout.hpp +++ b/phoenix/core/layout/vertical-layout.hpp @@ -1,7 +1,6 @@ struct VerticalLayout : public Layout { void append(Sizable& sizable, Size size, unsigned spacing = 0); void append(Sizable& sizable); - bool enabled(); Size minimumSize(); void remove(Sizable& sizable); void reset(); @@ -11,16 +10,12 @@ struct VerticalLayout : public Layout { void setMargin(unsigned margin); void setVisible(bool visible = true); void synchronizeLayout(); - bool visible(); - VerticalLayout(); ~VerticalLayout(); //private: struct State { - double alignment; - bool enabled; - unsigned margin; - bool visible; + double alignment = 0.0; + unsigned margin = 0; } state; struct Children { diff --git a/phoenix/core/state.hpp b/phoenix/core/state.hpp index aad8733b..a650c84d 100644 --- a/phoenix/core/state.hpp +++ b/phoenix/core/state.hpp @@ -5,7 +5,7 @@ struct Application::State { struct Timer::State { bool enabled = false; - unsigned milliseconds = 0; + unsigned interval = 0; }; struct BrowserWindow::State { @@ -52,12 +52,12 @@ struct Action::State { struct Menu::State { group action; - nall::image image = {0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0}; + nall::image image; string text; }; struct Item::State { - nall::image image = {0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0}; + nall::image image; string text; }; @@ -73,35 +73,48 @@ struct RadioItem::State { }; struct Sizable::State { - Layout* layout = nullptr; + bool enabled = true; + Sizable* parent = nullptr; + bool visible = true; Window* window = nullptr; }; struct Layout::State { + Widget* widget = nullptr; + unsigned widgetSelection = 0; }; struct Widget::State { bool abstract = false; - bool enabled = true; string font; Geometry geometry = {0, 0, 0, 0}; - bool visible = true; }; struct Button::State { - nall::image image = {0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0}; + nall::image image; Orientation orientation = Orientation::Horizontal; string text; }; struct Canvas::State { + Color color; uint32_t* data = nullptr; bool droppable = false; - unsigned width = 256; - unsigned height = 256; + vector gradient = {{}, {}, {}, {}}; + nall::image image; + Canvas::Mode mode = Canvas::Mode::Color; + unsigned width = 0; + unsigned height = 0; }; struct CheckButton::State { + bool checked = false; + nall::image image; + Orientation orientation = Orientation::Horizontal; + string text; +}; + +struct CheckLabel::State { bool checked = false; string text; }; @@ -114,6 +127,11 @@ struct ComboButton::State { struct Console::State { }; +struct Frame::State { + Layout* layout = nullptr; + string text; +}; + struct HexEdit::State { unsigned columns = 16; unsigned length = 0; @@ -158,9 +176,24 @@ struct ProgressBar::State { struct RadioButton::State { bool checked = true; nall::group group; + nall::image image; + Orientation orientation = Orientation::Horizontal; string text; }; +struct RadioLabel::State { + bool checked = true; + nall::group group; + string text; +}; + +struct TabFrame::State { + vector image; + vector layout; + unsigned selection = 0; + lstring text; +}; + struct TextEdit::State { unsigned cursorPosition = 0; bool editable = true; diff --git a/phoenix/core/utility.cpp b/phoenix/core/utility.cpp new file mode 100644 index 00000000..e69de29b diff --git a/phoenix/gtk/action/check-item.cpp b/phoenix/gtk/action/check-item.cpp index 5f0d85bb..59d6299b 100644 --- a/phoenix/gtk/action/check-item.cpp +++ b/phoenix/gtk/action/check-item.cpp @@ -1,11 +1,8 @@ namespace phoenix { -static void CheckItem_toggle(CheckItem* self) { - if(self->p.locked == false && self->onToggle) self->onToggle(); -} - -bool pCheckItem::checked() { - return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); +static void CheckItem_toggle(GtkCheckMenuItem* gtkCheckMenuItem, CheckItem* self) { + self->state.checked = gtk_check_menu_item_get_active(gtkCheckMenuItem); + if(!self->p.locked && self->onToggle) self->onToggle(); } void pCheckItem::setChecked(bool checked) { @@ -22,7 +19,7 @@ void pCheckItem::constructor() { widget = gtk_check_menu_item_new_with_mnemonic(""); setChecked(checkItem.state.checked); setText(checkItem.state.text); - g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(CheckItem_toggle), (gpointer)&checkItem); + g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(CheckItem_toggle), (gpointer)&checkItem); } void pCheckItem::destructor() { diff --git a/phoenix/gtk/action/radio-item.cpp b/phoenix/gtk/action/radio-item.cpp index 72ffe8e4..f00f9a76 100644 --- a/phoenix/gtk/action/radio-item.cpp +++ b/phoenix/gtk/action/radio-item.cpp @@ -1,22 +1,18 @@ namespace phoenix { -static void RadioItem_activate(RadioItem* self) { - for(auto& item : self->state.group) item.state.checked = (&item == self); - if(self->p.locked == false && self->checked() && self->onActivate) self->onActivate(); -} - -bool pRadioItem::checked() { - return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); +static void RadioItem_activate(GtkCheckMenuItem* gtkCheckMenuItem, RadioItem* self) { + self->p.onActivate(); } void pRadioItem::setChecked() { - locked = true; + parent().locked = true; for(auto& item : radioItem.state.group) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item.p.widget), false); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), true); - locked = false; + parent().locked = false; } void pRadioItem::setGroup(const group& group) { + parent().locked = true; for(auto& item : group) { if(&item == &group.first()) continue; GSList* currentGroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(group.first().p.widget)); @@ -24,6 +20,7 @@ void pRadioItem::setGroup(const group& group) { gtk_radio_menu_item_set_group(GTK_RADIO_MENU_ITEM(item.p.widget), currentGroup); } } + parent().locked = false; } void pRadioItem::setText(string text) { @@ -37,7 +34,7 @@ void pRadioItem::constructor() { for(auto& item : radioItem.state.group) { gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item.p.widget), item.state.checked); } - g_signal_connect_swapped(G_OBJECT(widget), "toggled", G_CALLBACK(RadioItem_activate), (gpointer)&radioItem); + g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(RadioItem_activate), (gpointer)&radioItem); } void pRadioItem::destructor() { @@ -49,4 +46,17 @@ void pRadioItem::orphan() { constructor(); } +void pRadioItem::onActivate() { + if(parent().locked) return; + bool wasChecked = radioItem.state.checked; + radioItem.setChecked(); + if(wasChecked) return; + if(radioItem.onActivate) radioItem.onActivate(); +} + +pRadioItem& pRadioItem::parent() { + if(radioItem.state.group.size()) return radioItem.state.group.first().p; + return *this; +} + } diff --git a/phoenix/gtk/platform.cpp b/phoenix/gtk/platform.cpp index 250fdc83..84dfc197 100644 --- a/phoenix/gtk/platform.cpp +++ b/phoenix/gtk/platform.cpp @@ -23,8 +23,10 @@ #include "widget/button.cpp" #include "widget/canvas.cpp" #include "widget/check-button.cpp" +#include "widget/check-label.cpp" #include "widget/combo-button.cpp" #include "widget/console.cpp" +#include "widget/frame.cpp" #include "widget/hex-edit.cpp" #include "widget/horizontal-scroller.cpp" #include "widget/horizontal-slider.cpp" @@ -33,6 +35,8 @@ #include "widget/list-view.cpp" #include "widget/progress-bar.cpp" #include "widget/radio-button.cpp" +#include "widget/radio-label.cpp" +#include "widget/tab-frame.cpp" #include "widget/text-edit.cpp" #include "widget/vertical-scroller.cpp" #include "widget/vertical-slider.cpp" diff --git a/phoenix/gtk/platform.hpp b/phoenix/gtk/platform.hpp index e2df1487..ea8c6012 100644 --- a/phoenix/gtk/platform.hpp +++ b/phoenix/gtk/platform.hpp @@ -95,7 +95,7 @@ struct pTimer : public pObject { Timer& timer; void setEnabled(bool enabled); - void setInterval(unsigned milliseconds); + void setInterval(unsigned interval); pTimer(Timer& timer) : pObject(timer), timer(timer) {} void constructor(); @@ -117,7 +117,6 @@ struct pWindow : public pObject { void append(Layout& layout); void append(Menu& menu); void append(Widget& widget); - Color backgroundColor(); bool focused(); Geometry frameMargin(); Geometry geometry(); @@ -200,7 +199,6 @@ struct pItem : public pAction { struct pCheckItem : public pAction { CheckItem& checkItem; - bool checked(); void setChecked(bool checked); void setText(string text); @@ -213,7 +211,6 @@ struct pCheckItem : public pAction { struct pRadioItem : public pAction { RadioItem& radioItem; - bool checked(); void setChecked(); void setGroup(const group& group); void setText(string text); @@ -222,11 +219,15 @@ struct pRadioItem : public pAction { void constructor(); void destructor(); void orphan(); + void onActivate(); + pRadioItem& parent(); }; struct pSizable : public pObject { Sizable& sizable; + virtual Position displacement() { return {0, 0}; } + pSizable(Sizable &sizable) : pObject(sizable), sizable(sizable) {} }; @@ -238,16 +239,18 @@ struct pLayout : public pSizable { struct pWidget : public pSizable { Widget& widget; - GtkWidget* gtkWidget; + GtkWidget* gtkWidget = nullptr; + GtkWidget* gtkParent = nullptr; - bool enabled(); + virtual GtkWidget* container(Widget& widget); + virtual Position containerOffset(); virtual bool focused(); virtual Size minimumSize(); - void setEnabled(bool enabled); + virtual void setEnabled(bool enabled); virtual void setFocused(); virtual void setFont(string font); virtual void setGeometry(Geometry geometry); - void setVisible(bool visible); + virtual void setVisible(bool visible); pWidget(Widget& widget) : pSizable(widget), widget(widget) {} void constructor(); @@ -270,27 +273,49 @@ struct pButton : public pWidget { struct pCanvas : public pWidget { Canvas& canvas; - cairo_surface_t* surface; + GdkPixbuf* surface = nullptr; + unsigned surfaceWidth = 0; + unsigned surfaceHeight = 0; + Size minimumSize(); void setDroppable(bool droppable); + void setGeometry(Geometry geometry); + void setMode(Canvas::Mode mode); void setSize(Size size); - void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} void constructor(); void destructor(); void orphan(); + void onExpose(GdkEventExpose* event); + void rasterize(); + void redraw(); + void release(); }; struct pCheckButton : public pWidget { CheckButton& checkButton; - bool checked(); + Size minimumSize(); + void setChecked(bool checked); + void setImage(const image& image, Orientation orientation); + void setText(string text); + + pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} + void constructor(); + void destructor(); + void orphan(); + void onToggle(); +}; + +struct pCheckLabel : public pWidget { + CheckLabel& checkLabel; + Size minimumSize(); void setChecked(bool checked); void setText(string text); - pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} + pCheckLabel(CheckLabel& checkLabel) : pWidget(checkLabel), checkLabel(checkLabel) {} void constructor(); void destructor(); void orphan(); @@ -301,12 +326,11 @@ struct pComboButton : public pWidget { unsigned itemCounter; void append(string text); - void modify(unsigned row, string text); - void remove(unsigned row); Size minimumSize(); + void remove(unsigned selection); void reset(); - unsigned selection(); - void setSelection(unsigned row); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {} void constructor(); @@ -331,6 +355,22 @@ struct pConsole : public pWidget { void seekCursorToEnd(); }; +struct pFrame : public pWidget { + Frame& frame; + + GtkWidget* container(Widget& widget); + Position containerOffset(); + void setEnabled(bool enabled); + void setGeometry(Geometry geometry); + void setText(string text); + void setVisible(bool visible); + + pFrame(Frame& frame) : pWidget(frame), frame(frame) {} + void constructor(); + void destructor(); + void orphan(); +}; + struct pHexEdit : public pWidget { HexEdit& hexEdit; GtkWidget* container; @@ -364,7 +404,6 @@ struct pHorizontalScroller : public pWidget { HorizontalScroller& horizontalScroller; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -378,7 +417,6 @@ struct pHorizontalSlider : public pWidget { HorizontalSlider& horizontalSlider; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -429,20 +467,17 @@ struct pListView : public pWidget { void append(const lstring& text); void autoSizeColumns(); - bool checked(unsigned row); bool focused(); - void modify(unsigned row, const lstring& text); - void remove(unsigned row); + void remove(unsigned selection); void reset(); - bool selected(); - unsigned selection(); void setCheckable(bool checkable); - void setChecked(unsigned row, bool checked); + void setChecked(unsigned selection, bool checked); void setHeaderText(const lstring& text); void setHeaderVisible(bool visible); - void setImage(unsigned row, unsigned column, const nall::image& image); + void setImage(unsigned selection, unsigned position, const image& image); void setSelected(bool selected); void setSelection(unsigned row); + void setText(unsigned selection, unsigned position, string text); pListView(ListView& listView) : pWidget(listView), listView(listView) {} void constructor(); @@ -467,18 +502,65 @@ struct pProgressBar : public pWidget { struct pRadioButton : public pWidget { RadioButton& radioButton; - bool checked(); Size minimumSize(); void setChecked(); void setGroup(const group& group); + void setImage(const image& image, Orientation orientation); void setText(string text); pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {} - void onActivate(); - pRadioButton& parent(); void constructor(); void destructor(); void orphan(); + void onActivate(); + pRadioButton& parent(); +}; + +struct pRadioLabel : public pWidget { + RadioLabel& radioLabel; + + Size minimumSize(); + void setChecked(); + void setGroup(const group& group); + void setText(string text); + + pRadioLabel(RadioLabel& radioLabel) : pWidget(radioLabel), radioLabel(radioLabel) {} + void onActivate(); + pRadioLabel& parent(); + void constructor(); + void destructor(); + void orphan(); +}; + +struct pTabFrame : public pWidget { + TabFrame& tabFrame; + struct Tab { + GtkWidget* child; + GtkWidget* container; + GtkWidget* layout; + GtkWidget* image; + GtkWidget* title; + }; + vector tabs; + + void append(string text, const image& image); + GtkWidget* container(Widget& widget); + Position containerOffset(); + Position displacement(); + void remove(unsigned selection); + void setEnabled(bool enabled); + void setGeometry(Geometry geometry); + void setImage(unsigned selection, const image& image); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); + void setVisible(bool visible); + + pTabFrame(TabFrame& tabFrame) : pWidget(tabFrame), tabFrame(tabFrame) {} + void constructor(); + void destructor(); + void orphan(); + void setFont(string font); + void synchronizeLayout(); }; struct pTextEdit : public pWidget { @@ -503,7 +585,6 @@ struct pVerticalScroller : public pWidget { VerticalScroller& verticalScroller; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -517,7 +598,6 @@ struct pVerticalSlider : public pWidget { VerticalSlider& verticalSlider; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/gtk/timer.cpp b/phoenix/gtk/timer.cpp index 6811e014..3be9964f 100644 --- a/phoenix/gtk/timer.cpp +++ b/phoenix/gtk/timer.cpp @@ -7,7 +7,7 @@ static guint Timer_trigger(pTimer* self) { } //callback may have disabled timer, so check state again if(self->timer.state.enabled) { - g_timeout_add(self->timer.state.milliseconds, (GSourceFunc)Timer_trigger, (gpointer)self); + g_timeout_add(self->timer.state.interval, (GSourceFunc)Timer_trigger, (gpointer)self); } //kill this timer instance (it is spawned above if needed again) return false; @@ -15,11 +15,11 @@ static guint Timer_trigger(pTimer* self) { void pTimer::setEnabled(bool enabled) { if(enabled) { - g_timeout_add(timer.state.milliseconds, (GSourceFunc)Timer_trigger, (gpointer)this); + g_timeout_add(timer.state.interval, (GSourceFunc)Timer_trigger, (gpointer)this); } } -void pTimer::setInterval(unsigned milliseconds) { +void pTimer::setInterval(unsigned interval) { } void pTimer::constructor() { diff --git a/phoenix/gtk/utility.cpp b/phoenix/gtk/utility.cpp index 70930ff2..8f04f28e 100644 --- a/phoenix/gtk/utility.cpp +++ b/phoenix/gtk/utility.cpp @@ -12,7 +12,7 @@ static GdkColor CreateColor(uint8_t r, uint8_t g, uint8_t b) { static GdkPixbuf* CreatePixbuf(const nall::image& image, bool scale = false) { nall::image gdkImage = image; gdkImage.transform(0, 32, 255u << 24, 255u << 0, 255u << 8, 255u << 16); - if(scale) gdkImage.scale(15, 15, Interpolation::Linear); + if(scale) gdkImage.scale(15, 15); GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, gdkImage.width, gdkImage.height); memcpy(gdk_pixbuf_get_pixels(pixbuf), gdkImage.data, gdkImage.width * gdkImage.height * 4); @@ -46,6 +46,37 @@ static lstring DropPaths(GtkSelectionData* data) { return paths; } +static Position GetDisplacement(Sizable* sizable) { + Position position; + while(sizable->state.parent) { + Position displacement = sizable->state.parent->p.displacement(); + position.x += displacement.x; + position.y += displacement.y; + sizable = sizable->state.parent; + } + return position; +} + +static Layout* GetParentWidgetLayout(Sizable* sizable) { + while(sizable) { + if(sizable->state.parent && dynamic_cast(sizable->state.parent)) return (Layout*)sizable; + sizable = sizable->state.parent; + } + return nullptr; +} + +static Widget* GetParentWidget(Sizable* sizable) { + while(sizable) { + if(sizable->state.parent && dynamic_cast(sizable->state.parent)) return (Widget*)sizable->state.parent; + sizable = sizable->state.parent; + } + return nullptr; +} + +static bool HasParentWidget(Sizable* sizable) { + return GetParentWidget(sizable) != nullptr; +} + static Keyboard::Keycode Keysym(unsigned keysym) { switch(keysym) { case GDK_Escape: return Keyboard::Keycode::Escape; diff --git a/phoenix/gtk/widget/canvas.cpp b/phoenix/gtk/widget/canvas.cpp index bd7bbd97..4fad9909 100644 --- a/phoenix/gtk/widget/canvas.cpp +++ b/phoenix/gtk/widget/canvas.cpp @@ -1,83 +1,102 @@ namespace phoenix { -static gboolean Canvas_expose(GtkWidget* widget, GdkEvent* event, pCanvas* self) { - cairo_t* context = gdk_cairo_create(gtk_widget_get_window(widget)); - cairo_set_source_surface(context, self->surface, 0, 0); - cairo_paint(context); - cairo_destroy(context); - return true; -} - -static void Canvas_dropEvent(GtkWidget* widget, GdkDragContext* context, gint x, gint y, -GtkSelectionData* data, guint type, guint timestamp, Canvas* canvas) { - if(canvas->state.droppable == false) return; +static void Canvas_drop(GtkWidget* widget, GdkDragContext* context, signed x, signed y, +GtkSelectionData* data, unsigned type, unsigned timestamp, Canvas* canvas) { + if(!canvas->state.droppable) return; lstring paths = DropPaths(data); if(paths.empty()) return; if(canvas->onDrop) canvas->onDrop(paths); } -static gboolean Canvas_mouseLeave(GtkWidget* widget, GdkEventButton* event, pCanvas* self) { - if(self->canvas.onMouseLeave) self->canvas.onMouseLeave(); +static signed Canvas_expose(GtkWidget* widget, GdkEventExpose* event, Canvas* self) { + self->p.onExpose(event); return true; } -static gboolean Canvas_mouseMove(GtkWidget* widget, GdkEventButton* event, pCanvas* self) { - if(self->canvas.onMouseMove) self->canvas.onMouseMove({(signed)event->x, (signed)event->y}); +static signed Canvas_mouseLeave(GtkWidget* widget, GdkEventButton* event, Canvas* self) { + if(self->onMouseLeave) self->onMouseLeave(); return true; } -static gboolean Canvas_mousePress(GtkWidget* widget, GdkEventButton* event, pCanvas* self) { - if(self->canvas.onMousePress) switch(event->button) { - case 1: self->canvas.onMousePress(Mouse::Button::Left); break; - case 2: self->canvas.onMousePress(Mouse::Button::Middle); break; - case 3: self->canvas.onMousePress(Mouse::Button::Right); break; +static signed Canvas_mouseMove(GtkWidget* widget, GdkEventButton* event, Canvas* self) { + if(self->onMouseMove) self->onMouseMove({(signed)event->x, (signed)event->y}); + return true; +} + +static signed Canvas_mousePress(GtkWidget* widget, GdkEventButton* event, Canvas* self) { + if(self->onMousePress) switch(event->button) { + case 1: self->onMousePress(Mouse::Button::Left); break; + case 2: self->onMousePress(Mouse::Button::Middle); break; + case 3: self->onMousePress(Mouse::Button::Right); break; } return true; } -static gboolean Canvas_mouseRelease(GtkWidget* widget, GdkEventButton* event, pCanvas* self) { - if(self->canvas.onMouseRelease) switch(event->button) { - case 1: self->canvas.onMouseRelease(Mouse::Button::Left); break; - case 2: self->canvas.onMouseRelease(Mouse::Button::Middle); break; - case 3: self->canvas.onMouseRelease(Mouse::Button::Right); break; +static signed Canvas_mouseRelease(GtkWidget* widget, GdkEventButton* event, Canvas* self) { + if(self->onMouseRelease) switch(event->button) { + case 1: self->onMouseRelease(Mouse::Button::Left); break; + case 2: self->onMouseRelease(Mouse::Button::Middle); break; + case 3: self->onMouseRelease(Mouse::Button::Right); break; } return true; } +Size pCanvas::minimumSize() { + return {canvas.state.width, canvas.state.height}; +} + void pCanvas::setDroppable(bool droppable) { gtk_drag_dest_set(gtkWidget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY); if(droppable) gtk_drag_dest_add_uri_targets(gtkWidget); } -void pCanvas::setSize(Size size) { - cairo_surface_destroy(surface); - surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, canvas.state.width, canvas.state.height); +void pCanvas::setGeometry(Geometry geometry) { + if(canvas.state.width == 0 || canvas.state.height == 0) rasterize(); + unsigned width = canvas.state.width; + unsigned height = canvas.state.height; + if(width == 0) width = widget.state.geometry.width; + if(height == 0) height = widget.state.geometry.height; + + if(width < geometry.width) { + geometry.x += (geometry.width - width) / 2; + geometry.width = width; + } + + if(height < geometry.height) { + geometry.y += (geometry.height - height) / 2; + geometry.height = height; + } + + pWidget::setGeometry(geometry); } -void pCanvas::update() { - memcpy(cairo_image_surface_get_data(surface), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t)); - if(gtk_widget_get_realized(gtkWidget) == false) return; - gdk_window_invalidate_rect(gtk_widget_get_window(gtkWidget), nullptr, true); +void pCanvas::setMode(Canvas::Mode mode) { + rasterize(), redraw(); +} + +void pCanvas::setSize(Size size) { + rasterize(), redraw(); } void pCanvas::constructor() { - surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, canvas.state.width, canvas.state.height); - memcpy(cairo_image_surface_get_data(surface), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t)); gtkWidget = gtk_drawing_area_new(); - gtk_widget_set_double_buffered(gtkWidget, false); +//gtk_widget_set_double_buffered(gtkWidget, false); gtk_widget_add_events(gtkWidget, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK); - g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Canvas_dropEvent), (gpointer)&canvas); - g_signal_connect(G_OBJECT(gtkWidget), "button_press_event", G_CALLBACK(Canvas_mousePress), (gpointer)this); - g_signal_connect(G_OBJECT(gtkWidget), "button_release_event", G_CALLBACK(Canvas_mouseRelease), (gpointer)this); - g_signal_connect(G_OBJECT(gtkWidget), "expose_event", G_CALLBACK(Canvas_expose), (gpointer)this); - g_signal_connect(G_OBJECT(gtkWidget), "leave_notify_event", G_CALLBACK(Canvas_mouseLeave), (gpointer)this); - g_signal_connect(G_OBJECT(gtkWidget), "motion_notify_event", G_CALLBACK(Canvas_mouseMove), (gpointer)this); + g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Canvas_mousePress), (gpointer)&canvas); + g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Canvas_mouseRelease), (gpointer)&canvas); + g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Canvas_drop), (gpointer)&canvas); + g_signal_connect(G_OBJECT(gtkWidget), "expose-event", G_CALLBACK(Canvas_expose), (gpointer)&canvas); + g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Canvas_mouseLeave), (gpointer)&canvas); + g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Canvas_mouseMove), (gpointer)&canvas); + + setDroppable(canvas.state.droppable); + rasterize(), redraw(); } void pCanvas::destructor() { + release(); gtk_widget_destroy(gtkWidget); - cairo_surface_destroy(surface); } void pCanvas::orphan() { @@ -85,4 +104,74 @@ void pCanvas::orphan() { constructor(); } +void pCanvas::onExpose(GdkEventExpose* expose) { + if(surface) gdk_draw_pixbuf(gtk_widget_get_window(gtkWidget), nullptr, surface, 0, 0, 0, 0, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); +} + +void pCanvas::rasterize() { + unsigned width = canvas.state.width; + unsigned height = canvas.state.height; + if(width == 0) width = widget.state.geometry.width; + if(height == 0) height = widget.state.geometry.height; + + if(width != surfaceWidth || height != surfaceHeight) release(); + surfaceWidth = width; + surfaceHeight = height; + + if(width == 0 || height == 0) return; + + if(!surface) surface = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height); + uint32_t* buffer = (uint32_t*)gdk_pixbuf_get_pixels(surface); + + if(canvas.state.mode == Canvas::Mode::Color) { + uint32_t color = canvas.state.color.argb(); + for(unsigned n = 0; n < width * height; n++) buffer[n] = color; + } + + if(canvas.state.mode == Canvas::Mode::Gradient) { + nall::image image; + image.allocate(width, height); + image.gradient( + canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb() + ); + memcpy(buffer, image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Image) { + nall::image image = canvas.state.image; + image.scale(width, height); + image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0); + memcpy(buffer, image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Data) { + if(width == canvas.state.width && height == canvas.state.height) { + memcpy(buffer, canvas.state.data, width * height * sizeof(uint32_t)); + } else { + memset(buffer, 0x00, width * height * sizeof(uint32_t)); + } + } + + //ARGB -> ABGR conversion + for(unsigned n = 0; n < width * height; n++) { + uint32_t color = *buffer; + color = (color & 0xff00ff00) | ((color & 0xff0000) >> 16) | ((color & 0x0000ff) << 16); + *buffer++ = color; + } +} + +void pCanvas::redraw() { + if(gtk_widget_get_realized(gtkWidget)) { + gdk_window_invalidate_rect(gtk_widget_get_window(gtkWidget), nullptr, true); + } +} + +void pCanvas::release() { + if(surface == nullptr) return; + g_object_unref(surface); + surface = nullptr; + surfaceWidth = 0; + surfaceHeight = 0; +} + } diff --git a/phoenix/gtk/widget/check-button.cpp b/phoenix/gtk/widget/check-button.cpp index 2f3a9c1e..d72d91ff 100644 --- a/phoenix/gtk/widget/check-button.cpp +++ b/phoenix/gtk/widget/check-button.cpp @@ -1,17 +1,23 @@ namespace phoenix { -static void CheckButton_toggle(CheckButton* self) { - self->state.checked = self->checked(); - if(self->p.locked == false && self->onToggle) self->onToggle(); -} - -bool pCheckButton::checked() { - return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtkWidget)); +static void CheckButton_toggle(GtkToggleButton* toggleButton, CheckButton* self) { + self->p.onToggle(); } Size pCheckButton::minimumSize() { Size size = pFont::size(widget.state.font, checkButton.state.text); - return {size.width + 28, size.height + 4}; + + if(checkButton.state.orientation == Orientation::Horizontal) { + size.width += checkButton.state.image.width; + size.height = max(checkButton.state.image.height, size.height); + } + + if(checkButton.state.orientation == Orientation::Vertical) { + size.width = max(checkButton.state.image.width, size.width); + size.height += checkButton.state.image.height; + } + + return {size.width + 24, size.height + 12}; } void pCheckButton::setChecked(bool checked) { @@ -20,13 +26,27 @@ void pCheckButton::setChecked(bool checked) { locked = false; } +void pCheckButton::setImage(const image& image, Orientation orientation) { + if(image.empty() == false) { + GtkImage* gtkImage = CreateImage(image); + gtk_button_set_image(GTK_BUTTON(gtkWidget), (GtkWidget*)gtkImage); + } else { + gtk_button_set_image(GTK_BUTTON(gtkWidget), nullptr); + } + switch(orientation) { + case Orientation::Horizontal: gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_LEFT); break; + case Orientation::Vertical: gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_TOP); break; + } +} + void pCheckButton::setText(string text) { gtk_button_set_label(GTK_BUTTON(gtkWidget), text); + setFont(widget.state.font); } void pCheckButton::constructor() { - gtkWidget = gtk_check_button_new_with_label(""); - g_signal_connect_swapped(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(CheckButton_toggle), (gpointer)&checkButton); + gtkWidget = gtk_toggle_button_new(); + g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(CheckButton_toggle), (gpointer)&checkButton); setChecked(checkButton.state.checked); setText(checkButton.state.text); @@ -41,4 +61,10 @@ void pCheckButton::orphan() { constructor(); } +void pCheckButton::onToggle() { + if(locked) return; + checkButton.state.checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtkWidget)); + if(checkButton.onToggle) checkButton.onToggle(); +} + } diff --git a/phoenix/gtk/widget/check-label.cpp b/phoenix/gtk/widget/check-label.cpp new file mode 100644 index 00000000..6e59fbd6 --- /dev/null +++ b/phoenix/gtk/widget/check-label.cpp @@ -0,0 +1,40 @@ +namespace phoenix { + +static void CheckLabel_toggle(GtkToggleButton* toggleButton, CheckLabel* self) { + self->state.checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->p.gtkWidget)); + if(!self->p.locked && self->onToggle) self->onToggle(); +} + +Size pCheckLabel::minimumSize() { + Size size = pFont::size(widget.state.font, checkLabel.state.text); + return {size.width + 28, size.height + 4}; +} + +void pCheckLabel::setChecked(bool checked) { + locked = true; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), checked); + locked = false; +} + +void pCheckLabel::setText(string text) { + gtk_button_set_label(GTK_BUTTON(gtkWidget), text); +} + +void pCheckLabel::constructor() { + gtkWidget = gtk_check_button_new_with_label(""); + g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(CheckLabel_toggle), (gpointer)&checkLabel); + + setChecked(checkLabel.state.checked); + setText(checkLabel.state.text); +} + +void pCheckLabel::destructor() { + gtk_widget_destroy(gtkWidget); +} + +void pCheckLabel::orphan() { + destructor(); + constructor(); +} + +} diff --git a/phoenix/gtk/widget/combo-button.cpp b/phoenix/gtk/widget/combo-button.cpp index a3d7c859..ed523f1a 100644 --- a/phoenix/gtk/widget/combo-button.cpp +++ b/phoenix/gtk/widget/combo-button.cpp @@ -1,15 +1,15 @@ namespace phoenix { static void ComboButton_change(ComboButton* self) { - if(self->p.locked == false) { - self->state.selection = self->selection(); + if(!self->p.locked) { + self->state.selection = gtk_combo_box_get_active(GTK_COMBO_BOX(self->p.gtkWidget)); if(self->onChange) self->onChange(); } } void pComboButton::append(string text) { gtk_combo_box_append_text(GTK_COMBO_BOX(gtkWidget), text); - if(itemCounter++ == 0) setSelection(0); + if(itemCounter++ == 0) comboButton.setSelection(0); } Size pComboButton::minimumSize() { @@ -20,21 +20,14 @@ Size pComboButton::minimumSize() { return {maximumWidth + 44, size.height + 12}; } -void pComboButton::modify(unsigned row, string text) { +void pComboButton::remove(unsigned selection) { locked = true; - unsigned position = selection(); - gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), row); - gtk_combo_box_insert_text(GTK_COMBO_BOX(gtkWidget), row, text); - gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), position); + gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), selection); + itemCounter--; locked = false; -} -void pComboButton::remove(unsigned row) { - locked = true; - unsigned position = selection(); - gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), row); - if(position == row) gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), 0); - locked = false; + //when removing the actively selected item, reset the selection to the first entry + if(selection == comboButton.state.selection) comboButton.setSelection(0); } void pComboButton::reset() { @@ -44,13 +37,17 @@ void pComboButton::reset() { locked = false; } -unsigned pComboButton::selection() { - return gtk_combo_box_get_active(GTK_COMBO_BOX(gtkWidget)); +void pComboButton::setSelection(unsigned selection) { + locked = true; + gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), selection); + locked = false; } -void pComboButton::setSelection(unsigned row) { +void pComboButton::setText(unsigned selection, string text) { locked = true; - gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), row); + gtk_combo_box_remove_text(GTK_COMBO_BOX(gtkWidget), selection); + gtk_combo_box_insert_text(GTK_COMBO_BOX(gtkWidget), selection, text); + gtk_combo_box_set_active(GTK_COMBO_BOX(gtkWidget), comboButton.state.selection); locked = false; } diff --git a/phoenix/gtk/widget/frame.cpp b/phoenix/gtk/widget/frame.cpp new file mode 100644 index 00000000..f41e1c3c --- /dev/null +++ b/phoenix/gtk/widget/frame.cpp @@ -0,0 +1,48 @@ +namespace phoenix { + +GtkWidget* pFrame::container(Widget& widget) { + return gtk_widget_get_parent(gtkWidget); +} + +Position pFrame::containerOffset() { + return {0, 0}; +} + +void pFrame::setEnabled(bool enabled) { + if(frame.state.layout) frame.state.layout->setEnabled(frame.state.layout->enabled()); + pWidget::setEnabled(enabled); +} + +void pFrame::setGeometry(Geometry geometry) { + pWidget::setGeometry(geometry); + if(frame.state.layout == nullptr) return; + Size size = pFont::size(widget.state.font, frame.state.text); + if(frame.state.text.empty()) size.height = 8; + geometry.x += 2, geometry.width -= 5; + geometry.y += size.height - 1, geometry.height -= size.height + 2; + frame.state.layout->setGeometry(geometry); +} + +void pFrame::setText(string text) { + gtk_frame_set_label(GTK_FRAME(gtkWidget), text); +} + +void pFrame::setVisible(bool visible) { + if(frame.state.layout) frame.state.layout->setVisible(frame.state.layout->visible()); + pWidget::setVisible(visible); +} + +void pFrame::constructor() { + gtkWidget = gtk_frame_new(""); +} + +void pFrame::destructor() { + gtk_widget_destroy(gtkWidget); +} + +void pFrame::orphan() { + destructor(); + constructor(); +} + +} diff --git a/phoenix/gtk/widget/horizontal-scroller.cpp b/phoenix/gtk/widget/horizontal-scroller.cpp index 8c62744b..5a46879f 100644 --- a/phoenix/gtk/widget/horizontal-scroller.cpp +++ b/phoenix/gtk/widget/horizontal-scroller.cpp @@ -1,19 +1,16 @@ namespace phoenix { -static void HorizontalScroller_change(HorizontalScroller* self) { - if(self->state.position == self->position()) return; - self->state.position = self->position(); - if(self->p.locked == false && self->onChange) self->onChange(); +static void HorizontalScroller_change(GtkRange* gtkRange, HorizontalScroller* self) { + unsigned position = (unsigned)gtk_range_get_value(gtkRange); + if(self->state.position == position) return; + self->state.position = position; + if(!self->p.locked && self->onChange) self->onChange(); } Size pHorizontalScroller::minimumSize() { return {0, 20}; } -unsigned pHorizontalScroller::position() { - return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget)); -} - void pHorizontalScroller::setLength(unsigned length) { locked = true; length += length == 0; @@ -28,7 +25,7 @@ void pHorizontalScroller::setPosition(unsigned position) { void pHorizontalScroller::constructor() { gtkWidget = gtk_hscrollbar_new(0); - g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalScroller_change), (gpointer)&horizontalScroller); + g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalScroller_change), (gpointer)&horizontalScroller); setLength(horizontalScroller.state.length); setPosition(horizontalScroller.state.position); diff --git a/phoenix/gtk/widget/horizontal-slider.cpp b/phoenix/gtk/widget/horizontal-slider.cpp index a46e453c..2ef80d93 100644 --- a/phoenix/gtk/widget/horizontal-slider.cpp +++ b/phoenix/gtk/widget/horizontal-slider.cpp @@ -1,8 +1,9 @@ namespace phoenix { -static void HorizontalSlider_change(HorizontalSlider* self) { - if(self->state.position == self->position()) return; - self->state.position = self->position(); +static void HorizontalSlider_change(GtkRange* gtkRange, HorizontalSlider* self) { + unsigned position = (unsigned)gtk_range_get_value(gtkRange); + if(self->state.position == position) return; + self->state.position = position; if(self->onChange) self->onChange(); } @@ -10,10 +11,6 @@ Size pHorizontalSlider::minimumSize() { return {0, 20}; } -unsigned pHorizontalSlider::position() { - return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget)); -} - void pHorizontalSlider::setLength(unsigned length) { length += length == 0; gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1)); @@ -27,7 +24,7 @@ void pHorizontalSlider::setPosition(unsigned position) { void pHorizontalSlider::constructor() { gtkWidget = gtk_hscale_new_with_range(0, 100, 1); gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false); - g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)&horizontalSlider); + g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(HorizontalSlider_change), (gpointer)&horizontalSlider); setLength(horizontalSlider.state.length); setPosition(horizontalSlider.state.position); diff --git a/phoenix/gtk/widget/list-view.cpp b/phoenix/gtk/widget/list-view.cpp index eb65634f..54e3b121 100644 --- a/phoenix/gtk/widget/list-view.cpp +++ b/phoenix/gtk/widget/list-view.cpp @@ -1,57 +1,53 @@ namespace phoenix { -static void ListView_activate(ListView* self) { +static void ListView_activate(GtkTreeView* treeView, GtkTreePath* path, GtkTreeViewColumn* column, ListView* self) { + char* pathname = gtk_tree_path_to_string(path); + unsigned selection = decimal(pathname); + g_free(pathname); + self->state.selection = selection; if(self->onActivate) self->onActivate(); } -static void ListView_change(ListView* self) { - if(self->state.selected == false || self->state.selection != self->selection()) { +static void ListView_change(GtkTreeView* treeView, ListView* self) { + GtkTreeIter iter; + if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(treeView), 0, &iter)) return; //should not be possible + char* path = gtk_tree_model_get_string_from_iter(gtk_tree_view_get_model(treeView), &iter); + unsigned selection = decimal(path); + g_free(path); + + if(!self->state.selected || self->state.selection != selection) { self->state.selected = true; - self->state.selection = self->selection(); + self->state.selection = selection; if(self->onChange) self->onChange(); } } static void ListView_toggle(GtkCellRendererToggle* cell, gchar* path, ListView* self) { - unsigned row = decimal(path); - self->setChecked(row, !self->checked(row)); - if(self->onToggle) self->onToggle(row); + unsigned selection = decimal(path); + self->setChecked(selection, !self->checked(selection)); + if(self->onToggle) self->onToggle(selection); } void pListView::append(const lstring& text) { GtkTreeIter iter; gtk_list_store_append(store, &iter); - for(unsigned n = 0; n < text.size(); n++) gtk_list_store_set(store, &iter, 1 + n * 2 + 1, (const char*)text[n], -1); + for(unsigned n = 0; n < text.size(); n++) { + gtk_list_store_set(store, &iter, 1 + n * 2 + 1, (const char*)text[n], -1); + } } void pListView::autoSizeColumns() { gtk_tree_view_columns_autosize(GTK_TREE_VIEW(subWidget)); } -bool pListView::checked(unsigned row) { - GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); - GtkTreeIter iter; - bool state; - if(gtk_tree_model_get_iter_from_string(model, &iter, string(row)) == false) return false; - gtk_tree_model_get(model, &iter, 0, &state, -1); - return state; -} - bool pListView::focused() { return GTK_WIDGET_HAS_FOCUS(subWidget); } -void pListView::modify(unsigned row, const lstring& text) { +void pListView::remove(unsigned selection) { GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); GtkTreeIter iter; - gtk_tree_model_get_iter_from_string(model, &iter, string(row)); - for(unsigned n = 0; n < text.size(); n++) gtk_list_store_set(store, &iter, 1 + n * 2 + 1, (const char*)text[n], -1); -} - -void pListView::remove(unsigned row) { - GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); - GtkTreeIter iter; - gtk_tree_model_get_iter_from_string(model, &iter, string(row)); + gtk_tree_model_get_iter_from_string(model, &iter, string(selection)); gtk_list_store_remove(store, &iter); } @@ -65,30 +61,14 @@ void pListView::reset() { gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(gtkWidget), 0); } -bool pListView::selected() { - GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget)); - return gtk_tree_selection_get_selected(selection, 0, 0); -} - -unsigned pListView::selection() { - GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget)); - GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); - GtkTreeIter iter; - if(gtk_tree_selection_get_selected(selection, 0, &iter) == false) return listView.state.selection; - char* path = gtk_tree_model_get_string_from_iter(model, &iter); - unsigned row = decimal(path); - g_free(path); - return row; -} - void pListView::setCheckable(bool checkable) { gtk_cell_renderer_set_visible(column(0).checkbutton, checkable); } -void pListView::setChecked(unsigned row, bool checked) { +void pListView::setChecked(unsigned selection, bool checked) { GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); GtkTreeIter iter; - gtk_tree_model_get_iter_from_string(model, &iter, string(row)); + gtk_tree_model_get_iter_from_string(model, &iter, string(selection)); gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, checked, -1); } @@ -101,15 +81,15 @@ void pListView::setHeaderVisible(bool visible) { gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(subWidget), visible); } -void pListView::setImage(unsigned row, unsigned column, const nall::image& image) { +void pListView::setImage(unsigned selection, unsigned position, const image& image) { GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); GtkTreeIter iter; - gtk_tree_model_get_iter_from_string(model, &iter, string(row)); + gtk_tree_model_get_iter_from_string(model, &iter, string(selection)); if(image.empty() == false) { GdkPixbuf* pixbuf = CreatePixbuf(image, true); - gtk_list_store_set(store, &iter, 1 + column * 2, pixbuf, -1); + gtk_list_store_set(store, &iter, 1 + position * 2, pixbuf, -1); } else { - gtk_list_store_set(store, &iter, 1 + column * 2, nullptr, -1); + gtk_list_store_set(store, &iter, 1 + position * 2, nullptr, -1); } } @@ -122,13 +102,13 @@ void pListView::setSelected(bool selected) { } } -void pListView::setSelection(unsigned row) { - GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget)); +void pListView::setSelection(unsigned selection) { + GtkTreeSelection* treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(subWidget)); GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); - gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_unselect_all(treeSelection); GtkTreeIter iter; - if(gtk_tree_model_get_iter_from_string(model, &iter, string(row)) == false) return; - gtk_tree_selection_select_iter(selection, &iter); + if(gtk_tree_model_get_iter_from_string(model, &iter, string(selection)) == false) return; + gtk_tree_selection_select_iter(treeSelection, &iter); //scroll window to selected item char* path = gtk_tree_model_get_string_from_iter(model, &iter); @@ -138,6 +118,13 @@ void pListView::setSelection(unsigned row) { g_free(path); } +void pListView::setText(unsigned selection, unsigned position, string text) { + GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(subWidget)); + GtkTreeIter iter; + gtk_tree_model_get_iter_from_string(model, &iter, string(selection)); + gtk_list_store_set(store, &iter, 1 + position * 2 + 1, (const char*)text, -1); +} + void pListView::constructor() { gtkWidget = gtk_scrolled_window_new(0, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); @@ -190,8 +177,8 @@ void pListView::constructor() { gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(subWidget), headerText.size() >= 2); //two or more columns + checkbutton column gtk_tree_view_set_search_column(GTK_TREE_VIEW(subWidget), 2); - g_signal_connect_swapped(G_OBJECT(subWidget), "cursor-changed", G_CALLBACK(ListView_change), (gpointer)&listView); - g_signal_connect_swapped(G_OBJECT(subWidget), "row-activated", G_CALLBACK(ListView_activate), (gpointer)&listView); + g_signal_connect(G_OBJECT(subWidget), "cursor-changed", G_CALLBACK(ListView_change), (gpointer)&listView); + g_signal_connect(G_OBJECT(subWidget), "row-activated", G_CALLBACK(ListView_activate), (gpointer)&listView); gtk_widget_show(subWidget); diff --git a/phoenix/gtk/widget/radio-button.cpp b/phoenix/gtk/widget/radio-button.cpp index 4c80580e..ca10fc36 100644 --- a/phoenix/gtk/widget/radio-button.cpp +++ b/phoenix/gtk/widget/radio-button.cpp @@ -1,63 +1,63 @@ namespace phoenix { -static void RadioButton_activate(RadioButton* self) { +static void RadioButton_activate(GtkToggleButton* toggleButton, RadioButton* self) { self->p.onActivate(); } -bool pRadioButton::checked() { - return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtkWidget)); -} - Size pRadioButton::minimumSize() { Size size = pFont::size(widget.state.font, radioButton.state.text); - return {size.width + 28, size.height + 4}; + + if(radioButton.state.orientation == Orientation::Horizontal) { + size.width += radioButton.state.image.width; + size.height = max(radioButton.state.image.height, size.height); + } + + if(radioButton.state.orientation == Orientation::Vertical) { + size.width = max(radioButton.state.image.width, size.width); + size.height += radioButton.state.image.height; + } + + return {size.width + 24, size.height + 12}; } void pRadioButton::setChecked() { parent().locked = true; - for(auto& item : radioButton.state.group) item.state.checked = false; - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), radioButton.state.checked = true); + for(auto& item : radioButton.state.group) { + bool checked = &item == &radioButton; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item.p.gtkWidget), item.state.checked = checked); + } parent().locked = false; } void pRadioButton::setGroup(const group& group) { - if(&parent() == this) return; parent().locked = true; - gtk_radio_button_set_group( - GTK_RADIO_BUTTON(gtkWidget), - gtk_radio_button_get_group(GTK_RADIO_BUTTON(parent().gtkWidget)) - ); for(auto& item : radioButton.state.group) { - if(item.state.checked) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item.p.gtkWidget), true); - break; - } + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item.p.gtkWidget), item.state.checked); } parent().locked = false; } -void pRadioButton::setText(string text) { - gtk_button_set_label(GTK_BUTTON(gtkWidget), text); -} - -void pRadioButton::onActivate() { - if(parent().locked == false) { - bool wasChecked = radioButton.state.checked; - setChecked(); - if(wasChecked == false) { - if(radioButton.onActivate) radioButton.onActivate(); - } +void pRadioButton::setImage(const image& image, Orientation orientation) { + if(image.empty() == false) { + GtkImage* gtkImage = CreateImage(image); + gtk_button_set_image(GTK_BUTTON(gtkWidget), (GtkWidget*)gtkImage); + } else { + gtk_button_set_image(GTK_BUTTON(gtkWidget), nullptr); + } + switch(orientation) { + case Orientation::Horizontal: gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_LEFT); break; + case Orientation::Vertical: gtk_button_set_image_position(GTK_BUTTON(gtkWidget), GTK_POS_TOP); break; } } -pRadioButton& pRadioButton::parent() { - if(radioButton.state.group.size()) return radioButton.state.group.first().p; - return *this; +void pRadioButton::setText(string text) { + gtk_button_set_label(GTK_BUTTON(gtkWidget), text); + setFont(widget.state.font); } void pRadioButton::constructor() { - gtkWidget = gtk_radio_button_new_with_label(nullptr, ""); - g_signal_connect_swapped(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioButton_activate), (gpointer)&radioButton); + gtkWidget = gtk_toggle_button_new(); + g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioButton_activate), (gpointer)&radioButton); setGroup(radioButton.state.group); setText(radioButton.state.text); @@ -72,4 +72,18 @@ void pRadioButton::orphan() { constructor(); } +void pRadioButton::onActivate() { + if(parent().locked) return; + bool wasChecked = radioButton.state.checked; + setChecked(); + if(!wasChecked) { + if(radioButton.onActivate) radioButton.onActivate(); + } +} + +pRadioButton& pRadioButton::parent() { + if(radioButton.state.group.size()) return radioButton.state.group.first().p; + return *this; +} + } diff --git a/phoenix/gtk/widget/radio-label.cpp b/phoenix/gtk/widget/radio-label.cpp new file mode 100644 index 00000000..5e1b224d --- /dev/null +++ b/phoenix/gtk/widget/radio-label.cpp @@ -0,0 +1,69 @@ +namespace phoenix { + +static void RadioLabel_activate(GtkToggleButton* toggleButton, RadioLabel* self) { + self->p.onActivate(); +} + +Size pRadioLabel::minimumSize() { + Size size = pFont::size(widget.state.font, radioLabel.state.text); + return {size.width + 28, size.height + 4}; +} + +void pRadioLabel::setChecked() { + parent().locked = true; + for(auto& item : radioLabel.state.group) item.state.checked = false; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkWidget), radioLabel.state.checked = true); + parent().locked = false; +} + +void pRadioLabel::setGroup(const group& group) { + if(&parent() == this) return; + parent().locked = true; + gtk_radio_button_set_group( + GTK_RADIO_BUTTON(gtkWidget), + gtk_radio_button_get_group(GTK_RADIO_BUTTON(parent().gtkWidget)) + ); + for(auto& item : radioLabel.state.group) { + if(item.state.checked) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item.p.gtkWidget), true); + break; + } + } + parent().locked = false; +} + +void pRadioLabel::setText(string text) { + gtk_button_set_label(GTK_BUTTON(gtkWidget), text); +} + +void pRadioLabel::constructor() { + gtkWidget = gtk_radio_button_new_with_label(nullptr, ""); + g_signal_connect(G_OBJECT(gtkWidget), "toggled", G_CALLBACK(RadioLabel_activate), (gpointer)&radioLabel); + + setGroup(radioLabel.state.group); + setText(radioLabel.state.text); +} + +void pRadioLabel::destructor() { + gtk_widget_destroy(gtkWidget); +} + +void pRadioLabel::orphan() { + destructor(); + constructor(); +} + +void pRadioLabel::onActivate() { + if(parent().locked) return; + bool wasChecked = radioLabel.state.checked; + setChecked(); + if(wasChecked) return; + if(radioLabel.onActivate) radioLabel.onActivate(); +} + +pRadioLabel& pRadioLabel::parent() { + if(radioLabel.state.group.size()) return radioLabel.state.group.first().p; + return *this; +} + +} diff --git a/phoenix/gtk/widget/tab-frame.cpp b/phoenix/gtk/widget/tab-frame.cpp new file mode 100644 index 00000000..fc021044 --- /dev/null +++ b/phoenix/gtk/widget/tab-frame.cpp @@ -0,0 +1,125 @@ +namespace phoenix { + +static void TabFrame_change(GtkNotebook* notebook, GtkWidget* page, unsigned selection, TabFrame* self) { + self->state.selection = selection; + self->p.synchronizeLayout(); + if(self->onChange) self->onChange(); +} + +void pTabFrame::append(string text, const image& image) { + unsigned selection = tabFrame.state.text.size() - 1; + + Tab tab; + tab.child = gtk_fixed_new(); + tab.container = gtk_hbox_new(false, 0); + tab.image = gtk_image_new(); + tab.title = gtk_label_new(text); + tabs.append(tab); + + gtk_widget_show(tab.child); + gtk_widget_show(tab.container); + gtk_widget_show(tab.image); + gtk_widget_show(tab.title); + gtk_box_pack_start(GTK_BOX(tab.container), tab.image, false, false, 0); + gtk_box_pack_start(GTK_BOX(tab.container), tab.title, false, false, 2); + + gtk_notebook_append_page(GTK_NOTEBOOK(gtkWidget), tab.child, tab.container); + setFont(widget.state.font); + if(!image.empty()) setImage(selection, image); +} + +GtkWidget* pTabFrame::container(Widget& widget) { + Layout* widgetLayout = GetParentWidgetLayout(&widget); + unsigned selection = 0; + for(auto& layout : tabFrame.state.layout) { + if(layout == widgetLayout) return tabs[selection].child; + selection++; + } + return nullptr; +} + +Position pTabFrame::containerOffset() { + return {widget.state.geometry.x + 3, widget.state.geometry.y + 28}; +} + +Position pTabFrame::displacement() { + return {6, 31}; +} + +void pTabFrame::remove(unsigned selection) { + tabs.remove(selection); + gtk_notebook_remove_page(GTK_NOTEBOOK(gtkWidget), selection); +} + +void pTabFrame::setEnabled(bool enabled) { + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setEnabled(layout->enabled()); + } + pWidget::setEnabled(enabled); +} + +void pTabFrame::setGeometry(Geometry geometry) { + pWidget::setGeometry(geometry); + geometry.x += 1, geometry.width -= 5; + geometry.y += 26, geometry.height -= 31; + for(auto& layout : tabFrame.state.layout) { + if(layout == nullptr) continue; + layout->setGeometry(geometry); + } + synchronizeLayout(); +} + +void pTabFrame::setImage(unsigned selection, const image& image) { + nall::image copy = image; + unsigned size = pFont::size(widget.state.font, " ").height; + copy.scale(size, size); + GdkPixbuf* pixbuf = CreatePixbuf(copy); + gtk_image_set_from_pixbuf(GTK_IMAGE(tabs[selection].image), pixbuf); +} + +void pTabFrame::setSelection(unsigned selection) { + gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkWidget), selection); +} + +void pTabFrame::setText(unsigned selection, string text) { + gtk_label_set_text(GTK_LABEL(tabs[selection].title), text); +} + +void pTabFrame::setVisible(bool visible) { + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setVisible(layout->visible()); + } + pWidget::setVisible(visible); +} + +void pTabFrame::constructor() { + gtkWidget = gtk_notebook_new(); + gtk_notebook_set_show_border(GTK_NOTEBOOK(gtkWidget), false); + gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkWidget), GTK_POS_TOP); + g_signal_connect(G_OBJECT(gtkWidget), "switch-page", G_CALLBACK(TabFrame_change), (gpointer)&tabFrame); + + setSelection(tabFrame.state.selection); +} + +void pTabFrame::destructor() { + gtk_widget_destroy(gtkWidget); +} + +void pTabFrame::orphan() { + destructor(); + constructor(); +} + +void pTabFrame::setFont(string font) { + for(auto& tab : tabs) pFont::setFont(tab.title, font); +} + +void pTabFrame::synchronizeLayout() { + unsigned selection = 0; + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setVisible(selection == tabFrame.state.selection); + selection++; + } +} + +} diff --git a/phoenix/gtk/widget/vertical-scroller.cpp b/phoenix/gtk/widget/vertical-scroller.cpp index 34bf6ab6..68928ce3 100644 --- a/phoenix/gtk/widget/vertical-scroller.cpp +++ b/phoenix/gtk/widget/vertical-scroller.cpp @@ -1,19 +1,16 @@ namespace phoenix { -static void VerticalScroller_change(VerticalScroller* self) { - if(self->state.position == self->position()) return; - self->state.position = self->position(); - if(self->p.locked == false && self->onChange) self->onChange(); +static void VerticalScroller_change(GtkRange* gtkRange, VerticalScroller* self) { + unsigned position = (unsigned)gtk_range_get_value(gtkRange); + if(self->state.position == position) return; + self->state.position = position; + if(!self->p.locked && self->onChange) self->onChange(); } Size pVerticalScroller::minimumSize() { return {20, 0}; } -unsigned pVerticalScroller::position() { - return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget)); -} - void pVerticalScroller::setLength(unsigned length) { locked = true; length += length == 0; @@ -28,7 +25,7 @@ void pVerticalScroller::setPosition(unsigned position) { void pVerticalScroller::constructor() { gtkWidget = gtk_vscrollbar_new(0); - g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalScroller_change), (gpointer)&verticalScroller); + g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalScroller_change), (gpointer)&verticalScroller); setLength(verticalScroller.state.length); setPosition(verticalScroller.state.position); diff --git a/phoenix/gtk/widget/vertical-slider.cpp b/phoenix/gtk/widget/vertical-slider.cpp index a0c4390a..102b5c44 100644 --- a/phoenix/gtk/widget/vertical-slider.cpp +++ b/phoenix/gtk/widget/vertical-slider.cpp @@ -1,8 +1,9 @@ namespace phoenix { -static void VerticalSlider_change(VerticalSlider* self) { - if(self->state.position == self->position()) return; - self->state.position = self->position(); +static void VerticalSlider_change(GtkRange* gtkRange, VerticalSlider* self) { + unsigned position = (unsigned)gtk_range_get_value(gtkRange); + if(self->state.position == position) return; + self->state.position = position; if(self->onChange) self->onChange(); } @@ -10,10 +11,6 @@ Size pVerticalSlider::minimumSize() { return {20, 0}; } -unsigned pVerticalSlider::position() { - return (unsigned)gtk_range_get_value(GTK_RANGE(gtkWidget)); -} - void pVerticalSlider::setLength(unsigned length) { length += length == 0; gtk_range_set_range(GTK_RANGE(gtkWidget), 0, max(1u, length - 1)); @@ -27,7 +24,7 @@ void pVerticalSlider::setPosition(unsigned position) { void pVerticalSlider::constructor() { gtkWidget = gtk_vscale_new_with_range(0, 100, 1); gtk_scale_set_draw_value(GTK_SCALE(gtkWidget), false); - g_signal_connect_swapped(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)&verticalSlider); + g_signal_connect(G_OBJECT(gtkWidget), "value-changed", G_CALLBACK(VerticalSlider_change), (gpointer)&verticalSlider); setLength(verticalSlider.state.length); setPosition(verticalSlider.state.position); diff --git a/phoenix/gtk/widget/viewport.cpp b/phoenix/gtk/widget/viewport.cpp index d04cea9f..bc2474e6 100644 --- a/phoenix/gtk/widget/viewport.cpp +++ b/phoenix/gtk/widget/viewport.cpp @@ -51,10 +51,10 @@ void pViewport::constructor() { gtk_widget_add_events(gtkWidget, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK); g_signal_connect(G_OBJECT(gtkWidget), "drag-data-received", G_CALLBACK(Viewport_dropEvent), (gpointer)&viewport); - g_signal_connect(G_OBJECT(gtkWidget), "button_press_event", G_CALLBACK(Viewport_mousePress), (gpointer)this); - g_signal_connect(G_OBJECT(gtkWidget), "button_release_event", G_CALLBACK(Viewport_mouseRelease), (gpointer)this); - g_signal_connect(G_OBJECT(gtkWidget), "leave_notify_event", G_CALLBACK(Viewport_mouseLeave), (gpointer)this); - g_signal_connect(G_OBJECT(gtkWidget), "motion_notify_event", G_CALLBACK(Viewport_mouseMove), (gpointer)this); + g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Viewport_mousePress), (gpointer)this); + g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Viewport_mouseRelease), (gpointer)this); + g_signal_connect(G_OBJECT(gtkWidget), "leave-notify-event", G_CALLBACK(Viewport_mouseLeave), (gpointer)this); + g_signal_connect(G_OBJECT(gtkWidget), "motion-notify-event", G_CALLBACK(Viewport_mouseMove), (gpointer)this); GdkColor color; color.pixel = 0; diff --git a/phoenix/gtk/widget/widget.cpp b/phoenix/gtk/widget/widget.cpp index ecbfe899..9f1f1bad 100644 --- a/phoenix/gtk/widget/widget.cpp +++ b/phoenix/gtk/widget/widget.cpp @@ -1,7 +1,11 @@ namespace phoenix { -bool pWidget::enabled() { - return gtk_widget_get_sensitive(gtkWidget); +GtkWidget* pWidget::container(Widget& widget) { + return nullptr; +} + +Position pWidget::containerOffset() { + return {0, 0}; } bool pWidget::focused() { @@ -13,8 +17,9 @@ Size pWidget::minimumSize() { } void pWidget::setEnabled(bool enabled) { + if(!widget.parent()) enabled = false; if(widget.state.abstract) enabled = false; - if(sizable.state.layout && sizable.state.layout->enabled() == false) enabled = false; + if(!widget.enabledToAll()) enabled = false; gtk_widget_set_sensitive(gtkWidget, enabled); } @@ -27,20 +32,26 @@ void pWidget::setFont(string font) { } void pWidget::setGeometry(Geometry geometry) { - if(sizable.window() && sizable.window()->visible()) gtk_fixed_move(GTK_FIXED(sizable.window()->p.formContainer), gtkWidget, geometry.x, geometry.y); - unsigned width = (signed)geometry.width <= 0 ? 1U : geometry.width; - unsigned height = (signed)geometry.height <= 0 ? 1U : geometry.height; + Position displacement = GetDisplacement(&widget); + geometry.x -= displacement.x; + geometry.y -= displacement.y; + + if(gtkParent) gtk_fixed_move(GTK_FIXED(gtkParent), gtkWidget, geometry.x, geometry.y); + unsigned width = (signed)geometry.width <= 0 ? 1u : geometry.width; + unsigned height = (signed)geometry.height <= 0 ? 1u : geometry.height; gtk_widget_set_size_request(gtkWidget, width, height); + if(widget.onSize) widget.onSize(); } void pWidget::setVisible(bool visible) { + if(!widget.parent()) visible = false; if(widget.state.abstract) visible = false; - if(sizable.state.layout && sizable.state.layout->visible() == false) visible = false; + if(!widget.visibleToAll()) visible = false; gtk_widget_set_visible(gtkWidget, visible); } void pWidget::constructor() { - if(widget.state.abstract) gtkWidget = gtk_label_new(""); + if(widget.state.abstract) gtkWidget = gtk_fixed_new(); } void pWidget::destructor() { diff --git a/phoenix/gtk/window.cpp b/phoenix/gtk/window.cpp index 0d8626cb..5b77e161 100644 --- a/phoenix/gtk/window.cpp +++ b/phoenix/gtk/window.cpp @@ -17,7 +17,9 @@ static gboolean Window_expose(GtkWidget* widget, GdkEvent* event, Window* window double blue = (double)color.blue / 255.0; double alpha = (double)color.alpha / 255.0; - if(gdk_screen_is_composited(gdk_screen_get_default())) { + if(gdk_screen_is_composited(gdk_screen_get_default()) + && gdk_screen_get_rgba_colormap(gdk_screen_get_default()) + ) { cairo_set_source_rgba(context, red, green, blue, alpha); } else { cairo_set_source_rgb(context, red, green, blue); @@ -80,7 +82,7 @@ static gboolean Window_configure(GtkWidget* widget, GdkEvent* event, Window* win return false; } -static void Window_dropEvent(GtkWidget* widget, GdkDragContext* context, gint x, gint y, +static void Window_drop(GtkWidget* widget, GdkDragContext* context, gint x, gint y, GtkSelectionData* data, guint type, guint timestamp, Window* window) { if(window->state.droppable == false) return; lstring paths = DropPaths(data); @@ -88,13 +90,13 @@ GtkSelectionData* data, guint type, guint timestamp, Window* window) { if(window->onDrop) window->onDrop(paths); } -static gboolean Window_keyPressEvent(GtkWidget* widget, GdkEventKey* event, Window* window) { +static gboolean Window_keyPress(GtkWidget* widget, GdkEventKey* event, Window* window) { Keyboard::Keycode key = Keysym(event->keyval); if(key != Keyboard::Keycode::None && window->onKeyPress) window->onKeyPress(key); return false; } -static gboolean Window_keyReleaseEvent(GtkWidget* widget, GdkEventKey* event, Window* window) { +static gboolean Window_keyRelease(GtkWidget* widget, GdkEventKey* event, Window* window) { Keyboard::Keycode key = Keysym(event->keyval); if(key != Keyboard::Keycode::None && window->onKeyRelease) window->onKeyRelease(key); return false; @@ -140,8 +142,8 @@ void pWindow::append(Layout& layout) { } void pWindow::append(Menu& menu) { - if(window.state.menuFont != "") menu.p.setFont(window.state.menuFont); - else menu.p.setFont("Sans, 8"); + if(window.state.menuFont) menu.p.setFont(window.state.menuFont); + else menu.p.setFont(Font::sans(8)); gtk_menu_shell_append(GTK_MENU_SHELL(this->menu), menu.p.widget); gtk_widget_show(menu.p.widget); } @@ -151,22 +153,17 @@ void pWindow::append(Widget& widget) { widget.setFont(window.state.widgetFont); } - ((Sizable&)widget).state.window = &window; - gtk_fixed_put(GTK_FIXED(formContainer), widget.p.gtkWidget, 0, 0); - if(widget.state.font != "") widget.p.setFont(widget.state.font); - else if(window.state.widgetFont != "") widget.p.setFont(window.state.widgetFont); - else widget.p.setFont("Sans, 8"); - widget.setVisible(widget.visible()); -} + if(HasParentWidget(&widget)) { + widget.p.gtkParent = GetParentWidget(&widget)->p.container(widget); + } else { + widget.p.gtkParent = formContainer; + } -Color pWindow::backgroundColor() { - if(window.state.backgroundColorOverride) return window.state.backgroundColor; - return { - (uint8_t)(settings->window.backgroundColor >> 16), - (uint8_t)(settings->window.backgroundColor >> 8), - (uint8_t)(settings->window.backgroundColor >> 0), - 255 - }; + gtk_fixed_put(GTK_FIXED(widget.p.gtkParent), widget.p.gtkWidget, 0, 0); + if(widget.state.font) widget.p.setFont(widget.state.font); + else if(window.state.widgetFont) widget.p.setFont(window.state.widgetFont); + else widget.p.setFont(Font::sans(8)); + widget.setVisible(widget.visible()); } Geometry pWindow::frameMargin() { @@ -335,11 +332,9 @@ void pWindow::constructor() { } } - if(gdk_screen_is_composited(gdk_screen_get_default())) { - gtk_widget_set_colormap(widget, gdk_screen_get_rgba_colormap(gdk_screen_get_default())); - } else { - gtk_widget_set_colormap(widget, gdk_screen_get_rgb_colormap(gdk_screen_get_default())); - } + GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default()); + if(!colormap) colormap = gdk_screen_get_rgb_colormap(gdk_screen_get_default()); + if(colormap) gtk_widget_set_colormap(widget, colormap); gtk_window_set_resizable(GTK_WINDOW(widget), true); #if GTK_MAJOR_VERSION >= 3 @@ -370,18 +365,24 @@ void pWindow::constructor() { setTitle(""); setResizable(window.state.resizable); setGeometry(window.state.geometry); - setMenuFont("Sans, 8"); - setStatusFont("Sans, 8"); + setMenuFont(Font::sans(8)); + setStatusFont(Font::sans(8)); g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window); g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(Window_expose), (gpointer)&window); g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window); - g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(Window_dropEvent), (gpointer)&window); - g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(Window_keyPressEvent), (gpointer)&window); - g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(Window_keyPressEvent), (gpointer)&window); + g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(Window_drop), (gpointer)&window); + g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(Window_keyPress), (gpointer)&window); + g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(Window_keyPress), (gpointer)&window); g_signal_connect(G_OBJECT(formContainer), "size-allocate", G_CALLBACK(Window_sizeAllocate), (gpointer)&window); g_signal_connect(G_OBJECT(formContainer), "size-request", G_CALLBACK(Window_sizeRequest), (gpointer)&window); + + window.state.backgroundColor = Color( + (uint8_t)(settings->window.backgroundColor >> 16), + (uint8_t)(settings->window.backgroundColor >> 8), + (uint8_t)(settings->window.backgroundColor >> 0) + ); } unsigned pWindow::menuHeight() { diff --git a/phoenix/qt/action/check-item.cpp b/phoenix/qt/action/check-item.cpp index 3b011c1e..0a4daa94 100644 --- a/phoenix/qt/action/check-item.cpp +++ b/phoenix/qt/action/check-item.cpp @@ -1,9 +1,5 @@ namespace phoenix { -bool pCheckItem::checked() { - return qtAction->isChecked(); -} - void pCheckItem::setChecked(bool checked) { qtAction->setChecked(checked); } @@ -25,7 +21,7 @@ void pCheckItem::destructor() { } void pCheckItem::onToggle() { - checkItem.state.checked = checked(); + checkItem.state.checked = qtAction->isChecked(); if(checkItem.onToggle) checkItem.onToggle(); } diff --git a/phoenix/qt/action/radio-item.cpp b/phoenix/qt/action/radio-item.cpp index e43034f7..e7c9974b 100644 --- a/phoenix/qt/action/radio-item.cpp +++ b/phoenix/qt/action/radio-item.cpp @@ -1,9 +1,5 @@ namespace phoenix { -bool pRadioItem::checked() { - return qtAction->isChecked(); -} - void pRadioItem::setChecked() { locked = true; for(auto& item : radioItem.state.group) { @@ -37,9 +33,9 @@ void pRadioItem::destructor() { } void pRadioItem::onActivate() { - if(radioItem.state.checked == false) { + if(!radioItem.state.checked) { setChecked(); - if(locked == false && radioItem.onActivate) radioItem.onActivate(); + if(!locked && radioItem.onActivate) radioItem.onActivate(); } } diff --git a/phoenix/qt/platform.cpp b/phoenix/qt/platform.cpp index 1fb82a06..dfee8cd2 100644 --- a/phoenix/qt/platform.cpp +++ b/phoenix/qt/platform.cpp @@ -24,8 +24,10 @@ #include "widget/button.cpp" #include "widget/canvas.cpp" #include "widget/check-button.cpp" +#include "widget/check-label.cpp" #include "widget/combo-button.cpp" #include "widget/console.cpp" +#include "widget/frame.cpp" #include "widget/hex-edit.cpp" #include "widget/horizontal-scroller.cpp" #include "widget/horizontal-slider.cpp" @@ -34,6 +36,8 @@ #include "widget/list-view.cpp" #include "widget/progress-bar.cpp" #include "widget/radio-button.cpp" +#include "widget/radio-label.cpp" +#include "widget/tab-frame.cpp" #include "widget/text-edit.cpp" #include "widget/vertical-scroller.cpp" #include "widget/vertical-slider.cpp" diff --git a/phoenix/qt/platform.moc b/phoenix/qt/platform.moc index 3b26d92d..f0f87005 100644 --- a/phoenix/qt/platform.moc +++ b/phoenix/qt/platform.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'platform.moc.hpp' ** -** Created: Tue Nov 5 18:38:24 2013 +** Created: Fri Nov 22 09:50:07 2013 ** by: The Qt Meta Object Compiler version 63 (Qt 4.8.2) ** ** WARNING! All changes made in this file will be lost! @@ -536,13 +536,14 @@ static const uint qt_meta_data_phoenix__pCheckButton[] = { 0, // signalCount // slots: signature, parameters, type, tag, flags - 23, 22, 22, 22, 0x0a, + 31, 23, 22, 22, 0x0a, 0 // eod }; static const char qt_meta_stringdata_phoenix__pCheckButton[] = { - "phoenix::pCheckButton\0\0onToggle()\0" + "phoenix::pCheckButton\0\0checked\0" + "onToggle(bool)\0" }; void phoenix::pCheckButton::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) @@ -551,11 +552,10 @@ void phoenix::pCheckButton::qt_static_metacall(QObject *_o, QMetaObject::Call _c Q_ASSERT(staticMetaObject.cast(_o)); pCheckButton *_t = static_cast(_o); switch (_id) { - case 0: _t->onToggle(); break; + case 0: _t->onToggle((*reinterpret_cast< bool(*)>(_a[1]))); break; default: ; } } - Q_UNUSED(_a); } const QMetaObjectExtraData phoenix::pCheckButton::staticMetaObjectExtraData = { @@ -598,6 +598,82 @@ int phoenix::pCheckButton::qt_metacall(QMetaObject::Call _c, int _id, void **_a) } return _id; } +static const uint qt_meta_data_phoenix__pCheckLabel[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 22, 21, 21, 21, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_phoenix__pCheckLabel[] = { + "phoenix::pCheckLabel\0\0onToggle()\0" +}; + +void phoenix::pCheckLabel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + pCheckLabel *_t = static_cast(_o); + switch (_id) { + case 0: _t->onToggle(); break; + default: ; + } + } + Q_UNUSED(_a); +} + +const QMetaObjectExtraData phoenix::pCheckLabel::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject phoenix::pCheckLabel::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_phoenix__pCheckLabel, + qt_meta_data_phoenix__pCheckLabel, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &phoenix::pCheckLabel::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *phoenix::pCheckLabel::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *phoenix::pCheckLabel::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_phoenix__pCheckLabel)) + return static_cast(const_cast< pCheckLabel*>(this)); + if (!strcmp(_clname, "pWidget")) + return static_cast< pWidget*>(const_cast< pCheckLabel*>(this)); + return QObject::qt_metacast(_clname); +} + +int phoenix::pCheckLabel::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 1) + qt_static_metacall(this, _c, _id, _a); + _id -= 1; + } + return _id; +} static const uint qt_meta_data_phoenix__pComboButton[] = { // content: @@ -737,6 +813,69 @@ int phoenix::pConsole::qt_metacall(QMetaObject::Call _c, int _id, void **_a) return _id; return _id; } +static const uint qt_meta_data_phoenix__pFrame[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + 0 // eod +}; + +static const char qt_meta_stringdata_phoenix__pFrame[] = { + "phoenix::pFrame\0" +}; + +void phoenix::pFrame::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + Q_UNUSED(_o); + Q_UNUSED(_id); + Q_UNUSED(_c); + Q_UNUSED(_a); +} + +const QMetaObjectExtraData phoenix::pFrame::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject phoenix::pFrame::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_phoenix__pFrame, + qt_meta_data_phoenix__pFrame, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &phoenix::pFrame::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *phoenix::pFrame::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *phoenix::pFrame::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_phoenix__pFrame)) + return static_cast(const_cast< pFrame*>(this)); + if (!strcmp(_clname, "pWidget")) + return static_cast< pWidget*>(const_cast< pFrame*>(this)); + return QObject::qt_metacast(_clname); +} + +int phoenix::pFrame::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} static const uint qt_meta_data_phoenix__pHexEdit[] = { // content: @@ -1125,6 +1264,82 @@ int phoenix::pListView::qt_metacall(QMetaObject::Call _c, int _id, void **_a) } return _id; } +static const uint qt_meta_data_phoenix__pRadioLabel[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 22, 21, 21, 21, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_phoenix__pRadioLabel[] = { + "phoenix::pRadioLabel\0\0onActivate()\0" +}; + +void phoenix::pRadioLabel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + pRadioLabel *_t = static_cast(_o); + switch (_id) { + case 0: _t->onActivate(); break; + default: ; + } + } + Q_UNUSED(_a); +} + +const QMetaObjectExtraData phoenix::pRadioLabel::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject phoenix::pRadioLabel::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_phoenix__pRadioLabel, + qt_meta_data_phoenix__pRadioLabel, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &phoenix::pRadioLabel::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *phoenix::pRadioLabel::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *phoenix::pRadioLabel::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_phoenix__pRadioLabel)) + return static_cast(const_cast< pRadioLabel*>(this)); + if (!strcmp(_clname, "pWidget")) + return static_cast< pWidget*>(const_cast< pRadioLabel*>(this)); + return QObject::qt_metacast(_clname); +} + +int phoenix::pRadioLabel::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 1) + qt_static_metacall(this, _c, _id, _a); + _id -= 1; + } + return _id; +} static const uint qt_meta_data_phoenix__pRadioButton[] = { // content: @@ -1201,6 +1416,82 @@ int phoenix::pRadioButton::qt_metacall(QMetaObject::Call _c, int _id, void **_a) } return _id; } +static const uint qt_meta_data_phoenix__pTabFrame[] = { + + // content: + 6, // revision + 0, // classname + 0, 0, // classinfo + 1, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 30, 20, 19, 19, 0x0a, + + 0 // eod +}; + +static const char qt_meta_stringdata_phoenix__pTabFrame[] = { + "phoenix::pTabFrame\0\0selection\0" + "onChange(int)\0" +}; + +void phoenix::pTabFrame::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + Q_ASSERT(staticMetaObject.cast(_o)); + pTabFrame *_t = static_cast(_o); + switch (_id) { + case 0: _t->onChange((*reinterpret_cast< int(*)>(_a[1]))); break; + default: ; + } + } +} + +const QMetaObjectExtraData phoenix::pTabFrame::staticMetaObjectExtraData = { + 0, qt_static_metacall +}; + +const QMetaObject phoenix::pTabFrame::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_phoenix__pTabFrame, + qt_meta_data_phoenix__pTabFrame, &staticMetaObjectExtraData } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &phoenix::pTabFrame::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *phoenix::pTabFrame::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *phoenix::pTabFrame::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_phoenix__pTabFrame)) + return static_cast(const_cast< pTabFrame*>(this)); + if (!strcmp(_clname, "pWidget")) + return static_cast< pWidget*>(const_cast< pTabFrame*>(this)); + return QObject::qt_metacast(_clname); +} + +int phoenix::pTabFrame::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 1) + qt_static_metacall(this, _c, _id, _a); + _id -= 1; + } + return _id; +} static const uint qt_meta_data_phoenix__pTextEdit[] = { // content: diff --git a/phoenix/qt/platform.moc.hpp b/phoenix/qt/platform.moc.hpp index 7658af97..8ea492bf 100644 --- a/phoenix/qt/platform.moc.hpp +++ b/phoenix/qt/platform.moc.hpp @@ -94,7 +94,7 @@ public: QTimer* qtTimer; void setEnabled(bool enabled); - void setInterval(unsigned milliseconds); + void setInterval(unsigned interval); pTimer(Timer& timer) : pObject(timer), timer(timer) {} void constructor(); @@ -132,7 +132,6 @@ public: void append(Layout& layout); void append(Menu& menu); void append(Widget& widget); - Color backgroundColor(); Geometry frameMargin(); bool focused(); Geometry geometry(); @@ -222,7 +221,6 @@ public: CheckItem& checkItem; QAction* qtAction; - bool checked(); void setChecked(bool checked); void setText(string text); @@ -242,7 +240,6 @@ public: QAction* qtAction; QActionGroup* qtGroup; - bool checked(); void setChecked(); void setGroup(const group& group); void setText(string text); @@ -279,11 +276,11 @@ struct pWidget : public pSizable { bool focused(); virtual Size minimumSize(); - void setEnabled(bool enabled); + virtual void setEnabled(bool enabled); void setFocused(); void setFont(string font); - void setGeometry(Geometry geometry); - void setVisible(bool visible); + virtual void setGeometry(Geometry geometry); + virtual void setVisible(bool visible); pWidget(Widget& widget) : pSizable(widget), widget(widget) {} void constructor(); @@ -317,7 +314,9 @@ struct pCanvas : public QObject, public pWidget { public: Canvas& canvas; - QImage* qtImage; + QImage* surface = nullptr; + unsigned surfaceWidth = 0; + unsigned surfaceHeight = 0; struct QtCanvas : public QWidget { pCanvas& self; void dragEnterEvent(QDragEnterEvent*); @@ -332,13 +331,16 @@ public: QtCanvas* qtCanvas; void setDroppable(bool droppable); + void setGeometry(Geometry geometry); + void setMode(Canvas::Mode mode); void setSize(Size size); - void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} void constructor(); void destructor(); void orphan(); + void rasterize(); + void release(); public slots: }; @@ -348,14 +350,34 @@ struct pCheckButton : public QObject, public pWidget { public: CheckButton& checkButton; - QCheckBox* qtCheckButton; + QToolButton* qtCheckButton; + + Size minimumSize(); + void setChecked(bool checked); + void setImage(const image& image, Orientation orientation); + void setText(string text); + + pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} + void constructor(); + void destructor(); + void orphan(); + +public slots: + void onToggle(bool checked); +}; + +struct pCheckLabel : public QObject, public pWidget { + Q_OBJECT + +public: + CheckLabel& checkLabel; + QCheckBox* qtCheckLabel; - bool checked(); Size minimumSize(); void setChecked(bool checked); void setText(string text); - pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} + pCheckLabel(CheckLabel& checkLabel) : pWidget(checkLabel), checkLabel(checkLabel) {} void constructor(); void destructor(); void orphan(); @@ -372,12 +394,12 @@ public: QComboBox* qtComboButton; void append(string text); - void modify(unsigned row, string text); - void remove(unsigned row); Size minimumSize(); + void remove(unsigned selection); void reset(); unsigned selection(); - void setSelection(unsigned row); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {} void constructor(); @@ -411,6 +433,24 @@ public: void keyPressEvent(QKeyEvent*); }; +struct pFrame : public QObject, public pWidget { + Q_OBJECT + +public: + Frame& frame; + QGroupBox* qtFrame; + + void setEnabled(bool enabled); + void setGeometry(Geometry geometry); + void setText(string text); + void setVisible(bool visible); + + pFrame(Frame& frame) : pWidget(frame), frame(frame) {} + void constructor(); + void destructor(); + void orphan(); +}; + struct pHexEdit : public QObject, public pWidget { Q_OBJECT @@ -459,7 +499,6 @@ public: QScrollBar* qtScroller; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -480,7 +519,6 @@ public: QSlider* qtSlider; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -537,19 +575,16 @@ public: void append(const lstring& text); void autoSizeColumns(); - bool checked(unsigned row); - void modify(unsigned row, const lstring& text); - void remove(unsigned row); + void remove(unsigned selection); void reset(); - bool selected(); - unsigned selection(); void setCheckable(bool checkable); - void setChecked(unsigned row, bool checked); + void setChecked(unsigned selection, bool checked); void setHeaderText(const lstring& text); void setHeaderVisible(bool visible); - void setImage(unsigned row, unsigned column, const nall::image& image); + void setImage(unsigned selection, unsigned position, const image& image); void setSelected(bool selected); - void setSelection(unsigned row); + void setSelection(unsigned selection); + void setText(unsigned selection, unsigned position, string text); pListView(ListView& listView) : pWidget(listView), listView(listView) {} void constructor(); @@ -575,17 +610,40 @@ struct pProgressBar : public pWidget { void orphan(); }; +struct pRadioLabel : public QObject, public pWidget { + Q_OBJECT + +public: + RadioLabel& radioLabel; + QRadioButton* qtRadioLabel; + + bool checked(); + Size minimumSize(); + void setChecked(); + void setGroup(const group& group); + void setText(string text); + + pRadioLabel(RadioLabel& radioLabel) : pWidget(radioLabel), radioLabel(radioLabel) {} + pRadioLabel& parent(); + void constructor(); + void destructor(); + void orphan(); + +public slots: + void onActivate(); +}; + struct pRadioButton : public QObject, public pWidget { Q_OBJECT public: RadioButton& radioButton; - QRadioButton* qtRadioButton; + QToolButton* qtRadioButton; - bool checked(); Size minimumSize(); void setChecked(); void setGroup(const group& group); + void setImage(const image& image, Orientation orientation); void setText(string text); pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {} @@ -598,6 +656,32 @@ public slots: void onActivate(); }; +struct pTabFrame : public QObject, public pWidget { + Q_OBJECT + +public: + TabFrame& tabFrame; + QTabWidget* qtTabFrame; + + void append(string text, const image& image); + void remove(unsigned selection); + void setEnabled(bool enabled); + void setGeometry(Geometry geometry); + void setImage(unsigned selection, const image& image); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); + void setVisible(bool visible); + + pTabFrame(TabFrame& tabFrame) : pWidget(tabFrame), tabFrame(tabFrame) {} + void constructor(); + void destructor(); + void orphan(); + void synchronizeLayout(); + +public slots: + void onChange(int selection); +}; + struct pTextEdit : public QObject, public pWidget { Q_OBJECT @@ -628,7 +712,6 @@ public: QScrollBar* qtScroller; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -649,7 +732,6 @@ public: QSlider* qtSlider; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/qt/timer.cpp b/phoenix/qt/timer.cpp index db6bc52b..6ed9a3b8 100644 --- a/phoenix/qt/timer.cpp +++ b/phoenix/qt/timer.cpp @@ -8,8 +8,8 @@ void pTimer::setEnabled(bool enabled) { } } -void pTimer::setInterval(unsigned milliseconds) { - qtTimer->setInterval(milliseconds); +void pTimer::setInterval(unsigned interval) { + qtTimer->setInterval(interval); } void pTimer::constructor() { diff --git a/phoenix/qt/widget/canvas.cpp b/phoenix/qt/widget/canvas.cpp index a63ff0e3..648fc030 100644 --- a/phoenix/qt/widget/canvas.cpp +++ b/phoenix/qt/widget/canvas.cpp @@ -4,32 +4,48 @@ void pCanvas::setDroppable(bool droppable) { qtCanvas->setAcceptDrops(droppable); } -void pCanvas::setSize(Size size) { - delete qtImage; - qtImage = new QImage(size.width, size.height, QImage::Format_ARGB32); +void pCanvas::setGeometry(Geometry geometry) { + if(canvas.state.width == 0 || canvas.state.height == 0) rasterize(); + unsigned width = canvas.state.width; + unsigned height = canvas.state.height; + if(width == 0) width = widget.state.geometry.width; + if(height == 0) height = widget.state.geometry.height; + + if(width < geometry.width) { + geometry.x += (geometry.width - width) / 2; + geometry.width = width; + } + + if(height < geometry.height) { + geometry.y += (geometry.height - height) / 2; + geometry.height = height; + } + + pWidget::setGeometry(geometry); } -void pCanvas::update() { - uint32_t* dp = (uint32_t*)qtImage->bits(), *sp = (uint32_t*)canvas.state.data; - for(unsigned n = 0; n < canvas.state.width * canvas.state.height; n++) *dp++ = 0xff000000 | *sp++; +void pCanvas::setMode(Canvas::Mode mode) { + rasterize(); + qtCanvas->update(); +} + +void pCanvas::setSize(Size size) { + rasterize(); qtCanvas->update(); } void pCanvas::constructor() { qtWidget = qtCanvas = new QtCanvas(*this); qtCanvas->setMouseTracking(true); - qtImage = new QImage(canvas.state.width, canvas.state.height, QImage::Format_ARGB32); - memcpy(qtImage->bits(), canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t)); pWidget::synchronizeState(); - update(); + rasterize(), qtCanvas->update(); } void pCanvas::destructor() { + release(); delete qtCanvas; - delete qtImage; qtWidget = qtCanvas = nullptr; - qtImage = nullptr; } void pCanvas::orphan() { @@ -37,6 +53,53 @@ void pCanvas::orphan() { constructor(); } +void pCanvas::rasterize() { + unsigned width = canvas.state.width; + unsigned height = canvas.state.height; + if(width == 0) width = widget.state.geometry.width; + if(height == 0) height = widget.state.geometry.height; + + if(canvas.state.mode != Canvas::Mode::Color) { + if(width != surfaceWidth || height != surfaceHeight) release(); + if(!surface) surface = new QImage(width, height, QImage::Format_ARGB32); + } + + if(canvas.state.mode == Canvas::Mode::Gradient) { + nall::image image; + image.allocate(width, height); + image.gradient( + canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb() + ); + memcpy(surface->bits(), image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Image) { + nall::image image = canvas.state.image; + image.scale(width, height); + image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0); + memcpy(surface->bits(), image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Data) { + if(width == canvas.state.width && height == canvas.state.height) { + memcpy(surface->bits(), canvas.state.data, width * height * sizeof(uint32_t)); + } else { + memset(surface->bits(), 0x00, width * height * sizeof(uint32_t)); + } + } + + surfaceWidth = width; + surfaceHeight = height; +} + +void pCanvas::release() { + if(surface == nullptr) return; + delete surface; + surface = nullptr; + surfaceWidth = 0; + surfaceHeight = 0; +} + void pCanvas::QtCanvas::dragEnterEvent(QDragEnterEvent* event) { if(event->mimeData()->hasUrls()) { event->acceptProposedAction(); @@ -76,15 +139,14 @@ void pCanvas::QtCanvas::mouseReleaseEvent(QMouseEvent* event) { } void pCanvas::QtCanvas::paintEvent(QPaintEvent* event) { + Canvas& canvas = self.canvas; QPainter painter(self.qtCanvas); - painter.drawImage(0, 0, *self.qtImage); -//this will scale the source image to fit the target widget size (nearest-neighbor): -//painter.drawImage( -// QRect(0, 0, geometry().width(), geometry().height()), -// *self.qtImage, -// QRect(0, 0, self.canvas.state.width, self.canvas.state.height) -//); + if(canvas.state.mode == Canvas::Mode::Color) { + painter.fillRect(event->rect(), QBrush(QColor(canvas.state.color.red, canvas.state.color.green, canvas.state.color.blue, canvas.state.color.alpha))); + } else { + painter.drawImage(0, 0, *self.surface); + } } pCanvas::QtCanvas::QtCanvas(pCanvas& self) : self(self) { diff --git a/phoenix/qt/widget/check-button.cpp b/phoenix/qt/widget/check-button.cpp index 93b4e1c9..921dc15d 100644 --- a/phoenix/qt/widget/check-button.cpp +++ b/phoenix/qt/widget/check-button.cpp @@ -1,12 +1,19 @@ namespace phoenix { -bool pCheckButton::checked() { - return qtCheckButton->isChecked(); -} - Size pCheckButton::minimumSize() { Size size = pFont::size(qtWidget->font(), checkButton.state.text); - return {size.width + 26, size.height + 6}; + + if(checkButton.state.orientation == Orientation::Horizontal) { + size.width += checkButton.state.image.width; + size.height = max(checkButton.state.image.height, size.height); + } + + if(checkButton.state.orientation == Orientation::Vertical) { + size.width = max(checkButton.state.image.width, size.width); + size.height += checkButton.state.image.height; + } + + return {size.width + 20, size.height + 12}; } void pCheckButton::setChecked(bool checked) { @@ -15,13 +22,25 @@ void pCheckButton::setChecked(bool checked) { locked = false; } +void pCheckButton::setImage(const image& image, Orientation orientation) { + qtCheckButton->setIconSize(QSize(image.width, image.height)); + qtCheckButton->setIcon(CreateIcon(image)); + qtCheckButton->setStyleSheet("text-align: top;"); + switch(orientation) { + case Orientation::Horizontal: qtCheckButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); break; + case Orientation::Vertical: qtCheckButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); break; + } +} + void pCheckButton::setText(string text) { qtCheckButton->setText(QString::fromUtf8(text)); } void pCheckButton::constructor() { - qtWidget = qtCheckButton = new QCheckBox; - connect(qtCheckButton, SIGNAL(stateChanged(int)), SLOT(onToggle())); + qtWidget = qtCheckButton = new QToolButton; + qtCheckButton->setCheckable(true); + qtCheckButton->setToolButtonStyle(Qt::ToolButtonTextOnly); + connect(qtCheckButton, SIGNAL(toggled(bool)), SLOT(onToggle(bool))); pWidget::synchronizeState(); setChecked(checkButton.state.checked); @@ -29,8 +48,6 @@ void pCheckButton::constructor() { } void pCheckButton::destructor() { - delete qtCheckButton; - qtWidget = qtCheckButton = nullptr; } void pCheckButton::orphan() { @@ -38,9 +55,9 @@ void pCheckButton::orphan() { constructor(); } -void pCheckButton::onToggle() { - checkButton.state.checked = checked(); - if(locked == false && checkButton.onToggle) checkButton.onToggle(); +void pCheckButton::onToggle(bool checked) { + checkButton.state.checked = checked; + if(!locked && checkButton.onToggle) checkButton.onToggle(); } } diff --git a/phoenix/qt/widget/check-label.cpp b/phoenix/qt/widget/check-label.cpp new file mode 100644 index 00000000..4e88e5b6 --- /dev/null +++ b/phoenix/qt/widget/check-label.cpp @@ -0,0 +1,42 @@ +namespace phoenix { + +Size pCheckLabel::minimumSize() { + Size size = pFont::size(qtWidget->font(), checkLabel.state.text); + return {size.width + 26, size.height + 6}; +} + +void pCheckLabel::setChecked(bool checked) { + locked = true; + qtCheckLabel->setChecked(checked); + locked = false; +} + +void pCheckLabel::setText(string text) { + qtCheckLabel->setText(QString::fromUtf8(text)); +} + +void pCheckLabel::constructor() { + qtWidget = qtCheckLabel = new QCheckBox; + connect(qtCheckLabel, SIGNAL(stateChanged(int)), SLOT(onToggle())); + + pWidget::synchronizeState(); + setChecked(checkLabel.state.checked); + setText(checkLabel.state.text); +} + +void pCheckLabel::destructor() { + delete qtCheckLabel; + qtWidget = qtCheckLabel = nullptr; +} + +void pCheckLabel::orphan() { + destructor(); + constructor(); +} + +void pCheckLabel::onToggle() { + checkLabel.state.checked = qtCheckLabel->isChecked(); + if(!locked && checkLabel.onToggle) checkLabel.onToggle(); +} + +} diff --git a/phoenix/qt/widget/combo-button.cpp b/phoenix/qt/widget/combo-button.cpp index 73433450..5b25bc79 100644 --- a/phoenix/qt/widget/combo-button.cpp +++ b/phoenix/qt/widget/combo-button.cpp @@ -13,16 +13,12 @@ Size pComboButton::minimumSize() { return {maximumWidth + 32, size.height + 12}; } -void pComboButton::modify(unsigned row, string text) { - qtComboButton->setItemText(row, text); -} - -void pComboButton::remove(unsigned row) { +void pComboButton::remove(unsigned selection) { locked = true; - unsigned position = selection(); - qtComboButton->removeItem(row); - if(position == row) qtComboButton->setCurrentIndex(0); + qtComboButton->removeItem(selection); locked = false; + + if(selection == comboButton.state.selection) comboButton.setSelection(0); } void pComboButton::reset() { @@ -31,15 +27,14 @@ void pComboButton::reset() { locked = false; } -unsigned pComboButton::selection() { - signed index = qtComboButton->currentIndex(); - return index >= 0 ? index : 0; +void pComboButton::setSelection(unsigned selection) { + locked = true; + qtComboButton->setCurrentIndex(selection); + locked = false; } -void pComboButton::setSelection(unsigned row) { - locked = true; - qtComboButton->setCurrentIndex(row); - locked = false; +void pComboButton::setText(unsigned selection, string text) { + qtComboButton->setItemText(selection, text); } void pComboButton::constructor() { @@ -51,7 +46,7 @@ void pComboButton::constructor() { locked = true; for(auto& text : comboButton.state.text) append(text); locked = false; - setSelection(selection); + comboButton.setSelection(selection); } void pComboButton::destructor() { @@ -65,8 +60,8 @@ void pComboButton::orphan() { } void pComboButton::onChange() { - comboButton.state.selection = selection(); - if(locked == false && comboButton.onChange) comboButton.onChange(); + comboButton.state.selection = qtComboButton->currentIndex(); + if(!locked && comboButton.onChange) comboButton.onChange(); } } diff --git a/phoenix/qt/widget/frame.cpp b/phoenix/qt/widget/frame.cpp new file mode 100644 index 00000000..25eb2c21 --- /dev/null +++ b/phoenix/qt/widget/frame.cpp @@ -0,0 +1,44 @@ +namespace phoenix { + +void pFrame::setEnabled(bool enabled) { + if(frame.state.layout) frame.state.layout->setEnabled(frame.state.layout->enabled()); + pWidget::setEnabled(enabled); +} + +void pFrame::setGeometry(Geometry geometry) { + pWidget::setGeometry(geometry); + if(frame.state.layout == nullptr) return; + Size size = pFont::size(widget.state.font, frame.state.text); + if(frame.state.text.empty()) size.height = 8; + geometry.x += 1, geometry.width -= 3; + geometry.y += size.height, geometry.height -= size.height + 1; + frame.state.layout->setGeometry(geometry); +} + +void pFrame::setText(string text) { + qtFrame->setTitle(QString::fromUtf8(text)); +} + +void pFrame::setVisible(bool visible) { + if(frame.state.layout) frame.state.layout->setVisible(frame.state.layout->visible()); + pWidget::setVisible(visible); +} + +void pFrame::constructor() { + qtWidget = qtFrame = new QGroupBox; + + pWidget::synchronizeState(); + setText(frame.state.text); +} + +void pFrame::destructor() { + delete qtFrame; + qtWidget = qtFrame = nullptr; +} + +void pFrame::orphan() { + destructor(); + constructor(); +} + +} diff --git a/phoenix/qt/widget/horizontal-scroller.cpp b/phoenix/qt/widget/horizontal-scroller.cpp index b60182ac..5ff5866e 100644 --- a/phoenix/qt/widget/horizontal-scroller.cpp +++ b/phoenix/qt/widget/horizontal-scroller.cpp @@ -4,10 +4,6 @@ Size pHorizontalScroller::minimumSize() { return {0, 15}; } -unsigned pHorizontalScroller::position() { - return qtScroller->value(); -} - void pHorizontalScroller::setLength(unsigned length) { length += length == 0; qtScroller->setRange(0, length - 1); @@ -40,7 +36,7 @@ void pHorizontalScroller::orphan() { } void pHorizontalScroller::onChange() { - horizontalScroller.state.position = position(); + horizontalScroller.state.position = qtScroller->value(); if(horizontalScroller.onChange) horizontalScroller.onChange(); } diff --git a/phoenix/qt/widget/horizontal-slider.cpp b/phoenix/qt/widget/horizontal-slider.cpp index b11cf779..5cfbdb9e 100644 --- a/phoenix/qt/widget/horizontal-slider.cpp +++ b/phoenix/qt/widget/horizontal-slider.cpp @@ -4,10 +4,6 @@ Size pHorizontalSlider::minimumSize() { return {0, 20}; } -unsigned pHorizontalSlider::position() { - return qtSlider->value(); -} - void pHorizontalSlider::setLength(unsigned length) { length += length == 0; qtSlider->setRange(0, length - 1); @@ -40,7 +36,7 @@ void pHorizontalSlider::orphan() { } void pHorizontalSlider::onChange() { - horizontalSlider.state.position = position(); + horizontalSlider.state.position = qtSlider->value(); if(horizontalSlider.onChange) horizontalSlider.onChange(); } diff --git a/phoenix/qt/widget/list-view.cpp b/phoenix/qt/widget/list-view.cpp index 9849e823..036f3f65 100644 --- a/phoenix/qt/widget/list-view.cpp +++ b/phoenix/qt/widget/list-view.cpp @@ -7,8 +7,8 @@ void pListView::append(const lstring& text) { item->setData(0, Qt::UserRole, (unsigned)items.size()); if(listView.state.checkable) item->setCheckState(0, Qt::Unchecked); - for(unsigned n = 0; n < text.size(); n++) { - item->setText(n, QString::fromUtf8(text[n])); + for(unsigned position = 0; position < text.size(); position++) { + item->setText(position, QString::fromUtf8(text[position])); } locked = false; } @@ -17,24 +17,9 @@ void pListView::autoSizeColumns() { for(unsigned n = 0; n < listView.state.headerText.size(); n++) qtListView->resizeColumnToContents(n); } -bool pListView::checked(unsigned row) { - QTreeWidgetItem* item = qtListView->topLevelItem(row); - return item ? item->checkState(0) == Qt::Checked : false; -} - -void pListView::modify(unsigned row, const lstring& text) { +void pListView::remove(unsigned selection) { locked = true; - QTreeWidgetItem* item = qtListView->topLevelItem(row); - if(item == nullptr) return; - for(unsigned n = 0; n < text.size(); n++) { - item->setText(n, QString::fromUtf8(text[n])); - } - locked = false; -} - -void pListView::remove(unsigned row) { - locked = true; - QTreeWidgetItem* item = qtListView->topLevelItem(row); + QTreeWidgetItem* item = qtListView->topLevelItem(selection); if(item == nullptr) return; delete item; locked = false; @@ -44,17 +29,6 @@ void pListView::reset() { qtListView->clear(); } -bool pListView::selected() { - QTreeWidgetItem* item = qtListView->currentItem(); - return (item && item->isSelected() == true); -} - -unsigned pListView::selection() { - QTreeWidgetItem* item = qtListView->currentItem(); - if(item == 0) return 0; - return item->data(0, Qt::UserRole).toUInt(); -} - void pListView::setCheckable(bool checkable) { if(checkable) { auto items = qtListView->findItems("", Qt::MatchContains); @@ -62,9 +36,9 @@ void pListView::setCheckable(bool checkable) { } } -void pListView::setChecked(unsigned row, bool checked) { +void pListView::setChecked(unsigned selection, bool checked) { locked = true; - QTreeWidgetItem* item = qtListView->topLevelItem(row); + QTreeWidgetItem* item = qtListView->topLevelItem(selection); if(item) item->setCheckState(0, checked ? Qt::Checked : Qt::Unchecked); locked = false; } @@ -84,11 +58,11 @@ void pListView::setHeaderVisible(bool visible) { autoSizeColumns(); } -void pListView::setImage(unsigned row, unsigned column, const nall::image& image) { - QTreeWidgetItem* item = qtListView->topLevelItem(row); +void pListView::setImage(unsigned selection, unsigned position, const nall::image& image) { + QTreeWidgetItem* item = qtListView->topLevelItem(selection); if(item) { - if(image.empty() == 0) item->setIcon(column, CreateIcon(image)); - if(image.empty() == 1) item->setIcon(column, QIcon()); + if(image.empty() == 0) item->setIcon(position, CreateIcon(image)); + if(image.empty() == 1) item->setIcon(position, QIcon()); } } @@ -97,18 +71,19 @@ void pListView::setSelected(bool selected) { if(item) item->setSelected(selected); } -void pListView::setSelection(unsigned row) { +void pListView::setSelection(unsigned selection) { locked = true; QTreeWidgetItem* item = qtListView->currentItem(); if(item) item->setSelected(false); - qtListView->setCurrentItem(0); - auto items = qtListView->findItems("", Qt::MatchContains); - for(unsigned n = 0; n < items.size(); n++) { - if(items[n]->data(0, Qt::UserRole).toUInt() == row) { - qtListView->setCurrentItem(items[n]); - break; - } - } + item = qtListView->topLevelItem(selection); + if(item) qtListView->setCurrentItem(item); + locked = false; +} + +void pListView::setText(unsigned selection, unsigned position, string text) { + locked = true; + QTreeWidgetItem* item = qtListView->topLevelItem(selection); + if(item) item->setText(position, QString::fromUtf8(text)); locked = false; } @@ -151,18 +126,25 @@ void pListView::onActivate() { } void pListView::onChange(QTreeWidgetItem* item) { - //Qt bug workaround: clicking items with mouse does not mark items as selected - if(item) item->setSelected(true); - listView.state.selected = selected(); - if(listView.state.selected) listView.state.selection = selection(); - if(locked == false && listView.onChange) listView.onChange(); + bool selected = listView.state.selected; + unsigned selection = listView.state.selection; + if(item) { + item->setSelected(true); //Qt bug workaround: clicking items with mouse does not mark items as selected + listView.state.selected = true; + listView.state.selection = item->data(0, Qt::UserRole).toUInt(); + } else { + listView.state.selected = false; + listView.state.selection = 0; + } + if(selected != listView.state.selected || selection != listView.state.selection) { + if(!locked && listView.onChange) listView.onChange(); + } } void pListView::onToggle(QTreeWidgetItem* item) { - unsigned row = item->data(0, Qt::UserRole).toUInt(); - bool checkState = checked(row); - listView.state.checked[row] = checkState; - if(locked == false && listView.onToggle) listView.onToggle(row); + unsigned selection = item->data(0, Qt::UserRole).toUInt(); + listView.state.checked[selection] = (item->checkState(0) == Qt::Checked); + if(!locked && listView.onToggle) listView.onToggle(selection); } } diff --git a/phoenix/qt/widget/radio-button.cpp b/phoenix/qt/widget/radio-button.cpp index 83c9cca2..b3554b85 100644 --- a/phoenix/qt/widget/radio-button.cpp +++ b/phoenix/qt/widget/radio-button.cpp @@ -1,20 +1,27 @@ namespace phoenix { -bool pRadioButton::checked() { - return qtRadioButton->isChecked(); -} - Size pRadioButton::minimumSize() { Size size = pFont::size(qtWidget->font(), radioButton.state.text); - return {size.width + 26, size.height + 6}; + + if(radioButton.state.orientation == Orientation::Horizontal) { + size.width += radioButton.state.image.width; + size.height = max(radioButton.state.image.height, size.height); + } + + if(radioButton.state.orientation == Orientation::Vertical) { + size.width = max(radioButton.state.image.width, size.width); + size.height += radioButton.state.image.height; + } + + return {size.width + 20, size.height + 12}; } void pRadioButton::setChecked() { parent().locked = true; - for(auto &item : radioButton.state.group) { - item.p.qtRadioButton->setChecked(item.state.checked = false); + for(auto& item : radioButton.state.group) { + bool checked = &item.p == this; + item.p.qtRadioButton->setChecked(item.state.checked = checked); } - qtRadioButton->setChecked(radioButton.state.checked = true); parent().locked = false; } @@ -26,6 +33,16 @@ void pRadioButton::setGroup(const group& group) { parent().locked = false; } +void pRadioButton::setImage(const image& image, Orientation orientation) { + qtRadioButton->setIconSize(QSize(image.width, image.height)); + qtRadioButton->setIcon(CreateIcon(image)); + qtRadioButton->setStyleSheet("text-align: top;"); + switch(orientation) { + case Orientation::Horizontal: qtRadioButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); break; + case Orientation::Vertical: qtRadioButton->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); break; + } +} + void pRadioButton::setText(string text) { qtRadioButton->setText(QString::fromUtf8(text)); } @@ -36,8 +53,9 @@ pRadioButton& pRadioButton::parent() { } void pRadioButton::constructor() { - qtWidget = qtRadioButton = new QRadioButton; - qtRadioButton->setAutoExclusive(false); + qtWidget = qtRadioButton = new QToolButton; + qtRadioButton->setCheckable(true); + qtRadioButton->setToolButtonStyle(Qt::ToolButtonTextOnly); connect(qtRadioButton, SIGNAL(toggled(bool)), SLOT(onActivate())); pWidget::synchronizeState(); @@ -56,12 +74,11 @@ void pRadioButton::orphan() { } void pRadioButton::onActivate() { - if(parent().locked == false) { - bool wasChecked = radioButton.state.checked; - setChecked(); - if(wasChecked == false) { - if(radioButton.onActivate) radioButton.onActivate(); - } + if(parent().locked) return; + bool wasChecked = radioButton.state.checked; + setChecked(); + if(!wasChecked) { + if(radioButton.onActivate) radioButton.onActivate(); } } diff --git a/phoenix/qt/widget/radio-label.cpp b/phoenix/qt/widget/radio-label.cpp new file mode 100644 index 00000000..927591a4 --- /dev/null +++ b/phoenix/qt/widget/radio-label.cpp @@ -0,0 +1,63 @@ +namespace phoenix { + +Size pRadioLabel::minimumSize() { + Size size = pFont::size(qtWidget->font(), radioLabel.state.text); + return {size.width + 26, size.height + 6}; +} + +void pRadioLabel::setChecked() { + parent().locked = true; + for(auto& item : radioLabel.state.group) { + bool checked = &item == &radioLabel; + item.p.qtRadioLabel->setChecked(item.state.checked = checked); + } + parent().locked = false; +} + +void pRadioLabel::setGroup(const group& group) { + parent().locked = true; + for(auto& item : radioLabel.state.group) { + item.p.qtRadioLabel->setChecked(item.state.checked); + } + parent().locked = false; +} + +void pRadioLabel::setText(string text) { + qtRadioLabel->setText(QString::fromUtf8(text)); +} + +pRadioLabel& pRadioLabel::parent() { + if(radioLabel.state.group.size()) return radioLabel.state.group.first().p; + return *this; +} + +void pRadioLabel::constructor() { + qtWidget = qtRadioLabel = new QRadioButton; + qtRadioLabel->setAutoExclusive(false); + connect(qtRadioLabel, SIGNAL(toggled(bool)), SLOT(onActivate())); + + pWidget::synchronizeState(); + setGroup(radioLabel.state.group); + setText(radioLabel.state.text); +} + +void pRadioLabel::destructor() { + if(qtRadioLabel) delete qtRadioLabel; + qtWidget = qtRadioLabel = nullptr; +} + +void pRadioLabel::orphan() { + destructor(); + constructor(); +} + +void pRadioLabel::onActivate() { + if(parent().locked) return; + bool wasChecked = radioLabel.state.checked; + setChecked(); + if(!wasChecked) { + if(radioLabel.onActivate) radioLabel.onActivate(); + } +} + +} diff --git a/phoenix/qt/widget/tab-frame.cpp b/phoenix/qt/widget/tab-frame.cpp new file mode 100644 index 00000000..14f04a53 --- /dev/null +++ b/phoenix/qt/widget/tab-frame.cpp @@ -0,0 +1,83 @@ +namespace phoenix { + +void pTabFrame::append(string text, const image& image) { + unsigned selection = tabFrame.state.text.size() - 1; + QWidget* widget = new QWidget; //addTab() requires a child widget, so give it an empty one + qtTabFrame->addTab(widget, QString::fromUtf8(text)); + if(!image.empty()) setImage(selection, image); +} + +void pTabFrame::remove(unsigned selection) { + qtTabFrame->removeTab(selection); +} + +void pTabFrame::setEnabled(bool enabled) { + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setEnabled(layout->enabled()); + } + pWidget::setEnabled(enabled); +} + +void pTabFrame::setGeometry(Geometry geometry) { + pWidget::setGeometry(geometry); + geometry.x += 1, geometry.width -= 2; + geometry.y += 29, geometry.height -= 30; + for(auto& layout : tabFrame.state.layout) { + if(layout == nullptr) continue; + layout->setGeometry(geometry); + } + synchronizeLayout(); +} + +void pTabFrame::setImage(unsigned selection, const image& image) { + qtTabFrame->setTabIcon(selection, CreateIcon(image)); +} + +void pTabFrame::setSelection(unsigned selection) { + qtTabFrame->setCurrentIndex(selection); + synchronizeLayout(); +} + +void pTabFrame::setText(unsigned selection, string text) { + qtTabFrame->setTabText(selection, QString::fromUtf8(text)); +} + +void pTabFrame::setVisible(bool visible) { + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setVisible(layout->visible()); + } + pWidget::setVisible(visible); +} + +void pTabFrame::constructor() { + qtWidget = qtTabFrame = new QTabWidget; + connect(qtTabFrame, SIGNAL(currentChanged(int)), SLOT(onChange(int))); + + setSelection(tabFrame.state.selection); +} + +void pTabFrame::destructor() { + delete qtTabFrame; + qtWidget = qtTabFrame = nullptr; +} + +void pTabFrame::orphan() { + destructor(); + constructor(); +} + +void pTabFrame::synchronizeLayout() { + unsigned selection = 0; + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setVisible(selection == tabFrame.state.selection); + selection++; + } +} + +void pTabFrame::onChange(int selection) { + tabFrame.state.selection = selection; + synchronizeLayout(); + if(tabFrame.onChange) tabFrame.onChange(); +} + +} diff --git a/phoenix/qt/widget/vertical-scroller.cpp b/phoenix/qt/widget/vertical-scroller.cpp index 3e11a8f7..ea8fac87 100644 --- a/phoenix/qt/widget/vertical-scroller.cpp +++ b/phoenix/qt/widget/vertical-scroller.cpp @@ -4,10 +4,6 @@ Size pVerticalScroller::minimumSize() { return {15, 0}; } -unsigned pVerticalScroller::position() { - return qtScroller->value(); -} - void pVerticalScroller::setLength(unsigned length) { length += length == 0; qtScroller->setRange(0, length - 1); @@ -40,7 +36,7 @@ void pVerticalScroller::orphan() { } void pVerticalScroller::onChange() { - verticalScroller.state.position = position(); + verticalScroller.state.position = qtScroller->value(); if(verticalScroller.onChange) verticalScroller.onChange(); } diff --git a/phoenix/qt/widget/vertical-slider.cpp b/phoenix/qt/widget/vertical-slider.cpp index 756a97ef..30fad0cd 100644 --- a/phoenix/qt/widget/vertical-slider.cpp +++ b/phoenix/qt/widget/vertical-slider.cpp @@ -4,10 +4,6 @@ Size pVerticalSlider::minimumSize() { return {20, 0}; } -unsigned pVerticalSlider::position() { - return qtSlider->value(); -} - void pVerticalSlider::setLength(unsigned length) { length += length == 0; qtSlider->setRange(0, length - 1); @@ -40,7 +36,7 @@ void pVerticalSlider::orphan() { } void pVerticalSlider::onChange() { - verticalSlider.state.position = position(); + verticalSlider.state.position = qtSlider->value(); if(verticalSlider.onChange) verticalSlider.onChange(); } diff --git a/phoenix/qt/widget/widget.cpp b/phoenix/qt/widget/widget.cpp index a0c991aa..22e368d7 100644 --- a/phoenix/qt/widget/widget.cpp +++ b/phoenix/qt/widget/widget.cpp @@ -9,8 +9,9 @@ Size pWidget::minimumSize() { } void pWidget::setEnabled(bool enabled) { + if(!widget.parent()) enabled = false; if(widget.state.abstract) enabled = false; - if(sizable.state.layout && sizable.state.layout->enabled() == false) enabled = false; + if(!widget.enabledToAll()) enabled = false; qtWidget->setEnabled(enabled); } @@ -24,12 +25,13 @@ void pWidget::setFont(string font) { void pWidget::setGeometry(Geometry geometry) { qtWidget->setGeometry(geometry.x, geometry.y, geometry.width, geometry.height); + if(widget.onSize) widget.onSize(); } void pWidget::setVisible(bool visible) { + if(!widget.parent()) visible = false; if(widget.state.abstract) visible = false; - if(sizable.state.layout == 0) visible = false; - if(sizable.state.layout && sizable.state.layout->visible() == false) visible = false; + if(!widget.visibleToAll()) visible = false; qtWidget->setVisible(visible); } @@ -40,9 +42,7 @@ void pWidget::constructor() { //pWidget::constructor() called before p{Derived}::constructor(); ergo qtWidget is not yet valid //pWidget::synchronizeState() is called to finish construction of p{Derived}::constructor() void pWidget::synchronizeState() { - setEnabled(widget.state.enabled); - setFont(widget.state.font); -//setVisible(widget.state.visible); + setFont(widget.font()); } void pWidget::destructor() { diff --git a/phoenix/qt/window.cpp b/phoenix/qt/window.cpp index 7971f9ad..47ad11a4 100644 --- a/phoenix/qt/window.cpp +++ b/phoenix/qt/window.cpp @@ -27,12 +27,6 @@ void pWindow::append(Widget& widget) { widget.setVisible(widget.visible()); } -Color pWindow::backgroundColor() { - if(window.state.backgroundColorOverride) return window.state.backgroundColor; - QColor color = qtWindow->palette().color(QPalette::ColorRole::Window); - return {(uint8_t)color.red(), (uint8_t)color.green(), (uint8_t)color.blue(), (uint8_t)color.alpha()}; -} - Geometry pWindow::frameMargin() { unsigned menuHeight = window.state.menuVisible ? settings->geometry.menuHeight : 0; unsigned statusHeight = window.state.statusVisible ? settings->geometry.statusHeight : 0; @@ -223,8 +217,11 @@ void pWindow::constructor() { qtLayout->addWidget(qtStatus); setGeometry(window.state.geometry); - setMenuFont("Sans, 8"); - setStatusFont("Sans, 8"); + setMenuFont(Font::sans(8)); + setStatusFont(Font::sans(8)); + + QColor color = qtWindow->palette().color(QPalette::ColorRole::Window); + window.state.backgroundColor = Color((uint8_t)color.red(), (uint8_t)color.green(), (uint8_t)color.blue(), (uint8_t)color.alpha()); } void pWindow::destructor() { diff --git a/phoenix/reference/action/check-item.cpp b/phoenix/reference/action/check-item.cpp index 9d439b98..f7a33af5 100644 --- a/phoenix/reference/action/check-item.cpp +++ b/phoenix/reference/action/check-item.cpp @@ -1,9 +1,5 @@ namespace phoenix { -bool pCheckItem::checked() { - return false; -} - void pCheckItem::setChecked(bool checked) { } diff --git a/phoenix/reference/action/check-item.hpp b/phoenix/reference/action/check-item.hpp index 0641a76b..3c1da56b 100644 --- a/phoenix/reference/action/check-item.hpp +++ b/phoenix/reference/action/check-item.hpp @@ -3,7 +3,6 @@ namespace phoenix { struct pCheckItem : public pAction { CheckItem& checkItem; - bool checked(); void setChecked(bool checked); void setText(string text); diff --git a/phoenix/reference/action/radio-item.cpp b/phoenix/reference/action/radio-item.cpp index bdd39e6e..e6fc1236 100644 --- a/phoenix/reference/action/radio-item.cpp +++ b/phoenix/reference/action/radio-item.cpp @@ -1,9 +1,5 @@ namespace phoenix { -bool pRadioItem::checked() { - return false; -} - void pRadioItem::setChecked() { } diff --git a/phoenix/reference/action/radio-item.hpp b/phoenix/reference/action/radio-item.hpp index 926a9883..c574341f 100644 --- a/phoenix/reference/action/radio-item.hpp +++ b/phoenix/reference/action/radio-item.hpp @@ -3,7 +3,6 @@ namespace phoenix { struct pRadioItem : public pAction { RadioItem& radioItem; - bool checked(); void setChecked(); void setGroup(const group& group); void setText(string text); diff --git a/phoenix/reference/platform.cpp b/phoenix/reference/platform.cpp index d49bb223..9ce2e44c 100644 --- a/phoenix/reference/platform.cpp +++ b/phoenix/reference/platform.cpp @@ -21,8 +21,10 @@ #include "widget/button.cpp" #include "widget/canvas.cpp" #include "widget/check-button.cpp" +#include "widget/check-label.cpp" #include "widget/combo-button.cpp" #include "widget/console.cpp" +#include "widget/frame.cpp" #include "widget/hex-edit.cpp" #include "widget/horizontal-scroller.cpp" #include "widget/horizontal-slider.cpp" @@ -31,6 +33,8 @@ #include "widget/list-view.cpp" #include "widget/progress-bar.cpp" #include "widget/radio-button.cpp" +#include "widget/radio-label.cpp" +#include "widget/tab-frame.cpp" #include "widget/text-edit.cpp" #include "widget/vertical-scroller.cpp" #include "widget/vertical-slider.cpp" diff --git a/phoenix/reference/platform.hpp b/phoenix/reference/platform.hpp index 297a6eb1..02c6364f 100644 --- a/phoenix/reference/platform.hpp +++ b/phoenix/reference/platform.hpp @@ -29,8 +29,10 @@ namespace phoenix { #include "widget/button.hpp" #include "widget/canvas.hpp" #include "widget/check-button.hpp" +#include "widget/check-label.hpp" #include "widget/combo-button.hpp" #include "widget/console.hpp" +#include "widget/frame.hpp" #include "widget/hex-edit.hpp" #include "widget/horizontal-scroller.hpp" #include "widget/horizontal-slider.hpp" @@ -39,6 +41,8 @@ namespace phoenix { #include "widget/list-view.hpp" #include "widget/progress-bar.hpp" #include "widget/radio-button.hpp" +#include "widget/radio-label.hpp" +#include "widget/tab-frame.hpp" #include "widget/text-edit.hpp" #include "widget/vertical-scroller.hpp" #include "widget/vertical-slider.hpp" diff --git a/phoenix/reference/timer.cpp b/phoenix/reference/timer.cpp index abe06080..1d24c5a5 100644 --- a/phoenix/reference/timer.cpp +++ b/phoenix/reference/timer.cpp @@ -3,7 +3,7 @@ namespace phoenix { void pTimer::setEnabled(bool enabled) { } -void pTimer::setInterval(unsigned milliseconds) { +void pTimer::setInterval(unsigned interval) { } void pTimer::constructor() { diff --git a/phoenix/reference/timer.hpp b/phoenix/reference/timer.hpp index 6893a26c..a924a9eb 100644 --- a/phoenix/reference/timer.hpp +++ b/phoenix/reference/timer.hpp @@ -4,7 +4,7 @@ struct pTimer : public pObject { Timer& timer; void setEnabled(bool enabled); - void setInterval(unsigned milliseconds); + void setInterval(unsigned interval); pTimer(Timer& timer) : pObject(timer), timer(timer) {} void constructor(); diff --git a/phoenix/reference/widget/canvas.cpp b/phoenix/reference/widget/canvas.cpp index 1f2a3988..b62da56a 100644 --- a/phoenix/reference/widget/canvas.cpp +++ b/phoenix/reference/widget/canvas.cpp @@ -3,10 +3,10 @@ namespace phoenix { void pCanvas::setDroppable(bool droppable) { } -void pCanvas::setSize(Size size) { +void pCanvas::setMode(Canvas::Mode mode) { } -void pCanvas::update() { +void pCanvas::setSize(Size size) { } void pCanvas::constructor() { diff --git a/phoenix/reference/widget/canvas.hpp b/phoenix/reference/widget/canvas.hpp index 98e5d520..67167cbd 100644 --- a/phoenix/reference/widget/canvas.hpp +++ b/phoenix/reference/widget/canvas.hpp @@ -4,8 +4,8 @@ struct pCanvas : public pWidget { Canvas& canvas; void setDroppable(bool droppable); + void setMode(Canvas::Mode mode); void setSize(Size size); - void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} void constructor(); diff --git a/phoenix/reference/widget/check-button.cpp b/phoenix/reference/widget/check-button.cpp index e237e6b1..ff13e279 100644 --- a/phoenix/reference/widget/check-button.cpp +++ b/phoenix/reference/widget/check-button.cpp @@ -1,10 +1,9 @@ namespace phoenix { -bool pCheckButton::checked() { - return false; +void pCheckButton::setChecked(bool checked) { } -void pCheckButton::setChecked(bool checked) { +void pCheckButton::setImage(const image& image, Orientation orientation) { } void pCheckButton::setText(string text) { diff --git a/phoenix/reference/widget/check-button.hpp b/phoenix/reference/widget/check-button.hpp index 62c4ed96..eae9ad26 100644 --- a/phoenix/reference/widget/check-button.hpp +++ b/phoenix/reference/widget/check-button.hpp @@ -3,8 +3,8 @@ namespace phoenix { struct pCheckButton : public pWidget { CheckButton& checkButton; - bool checked(); void setChecked(bool checked); + void setImage(const image& image, Orientation orientation); void setText(string text); pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} diff --git a/phoenix/reference/widget/check-label.cpp b/phoenix/reference/widget/check-label.cpp new file mode 100644 index 00000000..f3751203 --- /dev/null +++ b/phoenix/reference/widget/check-label.cpp @@ -0,0 +1,15 @@ +namespace phoenix { + +void pCheckLabel::setChecked(bool checked) { +} + +void pCheckLabel::setText(string text) { +} + +void pCheckLabel::constructor() { +} + +void pCheckLabel::destructor() { +} + +} diff --git a/phoenix/reference/widget/check-label.hpp b/phoenix/reference/widget/check-label.hpp new file mode 100644 index 00000000..254092f9 --- /dev/null +++ b/phoenix/reference/widget/check-label.hpp @@ -0,0 +1,14 @@ +namespace phoenix { + +struct pCheckLabel : public pWidget { + CheckLabel& checkLabel; + + void setChecked(bool checked); + void setText(string text); + + pCheckLabel(CheckLabel& checkLabel) : pWidget(checkLabel), checkLabel(checkLabel) {} + void constructor(); + void destructor(); +}; + +} diff --git a/phoenix/reference/widget/combo-button.cpp b/phoenix/reference/widget/combo-button.cpp index 67bcb9e1..0eac259b 100644 --- a/phoenix/reference/widget/combo-button.cpp +++ b/phoenix/reference/widget/combo-button.cpp @@ -3,20 +3,16 @@ namespace phoenix { void pComboButton::append(string text) { } -void pComboButton::modify(unsigned row, string text) { -} - -void pComboButton::remove(unsigned row) { +void pComboButton::remove(unsigned selection) { } void pComboButton::reset() { } -unsigned pComboButton::selection() { - return 0; +void pComboButton::setSelection(unsigned selection) { } -void pComboButton::setSelection(unsigned row) { +void pComboButton::setText(unsigned selection, string text) { } void pComboButton::constructor() { diff --git a/phoenix/reference/widget/combo-button.hpp b/phoenix/reference/widget/combo-button.hpp index 65cdcbbe..9c183481 100644 --- a/phoenix/reference/widget/combo-button.hpp +++ b/phoenix/reference/widget/combo-button.hpp @@ -4,11 +4,10 @@ struct pComboButton : public pWidget { ComboButton& comboButton; void append(string text); - void modify(unsigned row, string text); - void remove(unsigned row); + void remove(unsigned selection); void reset(); - unsigned selection(); - void setSelection(unsigned row); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {} void constructor(); diff --git a/phoenix/reference/widget/frame.cpp b/phoenix/reference/widget/frame.cpp new file mode 100644 index 00000000..cfac6257 --- /dev/null +++ b/phoenix/reference/widget/frame.cpp @@ -0,0 +1,12 @@ +namespace phoenix { + +void pFrame::setText(string text) { +} + +void pFrame::constructor() { +} + +void pFrame::destructor() { +} + +} diff --git a/phoenix/reference/widget/frame.hpp b/phoenix/reference/widget/frame.hpp new file mode 100644 index 00000000..f4477343 --- /dev/null +++ b/phoenix/reference/widget/frame.hpp @@ -0,0 +1,13 @@ +namespace phoenix { + +struct pFrame : public pWidget { + Frame& frame; + + void setText(string text); + + pFrame(Frame& frame) : pWidget(frame), frame(frame) {} + void constructor(); + void destructor(); +}; + +} diff --git a/phoenix/reference/widget/horizontal-scroller.cpp b/phoenix/reference/widget/horizontal-scroller.cpp index 7f60a1bd..6bd66d06 100644 --- a/phoenix/reference/widget/horizontal-scroller.cpp +++ b/phoenix/reference/widget/horizontal-scroller.cpp @@ -1,9 +1,5 @@ namespace phoenix { -unsigned pHorizontalScroller::position() { - return 0; -} - void pHorizontalScroller::setLength(unsigned length) { } diff --git a/phoenix/reference/widget/horizontal-scroller.hpp b/phoenix/reference/widget/horizontal-scroller.hpp index 7205c3c4..8649b420 100644 --- a/phoenix/reference/widget/horizontal-scroller.hpp +++ b/phoenix/reference/widget/horizontal-scroller.hpp @@ -3,7 +3,6 @@ namespace phoenix { struct pHorizontalScroller : public pWidget { HorizontalScroller& horizontalScroller; - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/reference/widget/horizontal-slider.cpp b/phoenix/reference/widget/horizontal-slider.cpp index bb926f04..96c2f8c5 100644 --- a/phoenix/reference/widget/horizontal-slider.cpp +++ b/phoenix/reference/widget/horizontal-slider.cpp @@ -1,9 +1,5 @@ namespace phoenix { -unsigned pHorizontalSlider::position() { - return 0; -} - void pHorizontalSlider::setLength(unsigned length) { } diff --git a/phoenix/reference/widget/horizontal-slider.hpp b/phoenix/reference/widget/horizontal-slider.hpp index aaeb4297..2bb7a402 100644 --- a/phoenix/reference/widget/horizontal-slider.hpp +++ b/phoenix/reference/widget/horizontal-slider.hpp @@ -3,7 +3,6 @@ namespace phoenix { struct pHorizontalSlider : public pWidget { HorizontalSlider& horizontalSlider; - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/reference/widget/list-view.cpp b/phoenix/reference/widget/list-view.cpp index f7eb6f61..d90683b4 100644 --- a/phoenix/reference/widget/list-view.cpp +++ b/phoenix/reference/widget/list-view.cpp @@ -6,30 +6,16 @@ void pListView::append(const lstring& text) { void pListView::autoSizeColumns() { } -bool pListView::checked(unsigned row) { -} - -void pListView::modify(unsigned row, const lstring& text) { -} - -void pListView::remove(unsigned row) { +void pListView::remove(unsigned selection) { } void pListView::reset() { } -bool pListView::selected() { - return false; -} - -unsigned pListView::selection() { - return 0; -} - void pListView::setCheckable(bool checkable) { } -void pListView::setChecked(unsigned row, bool checked) { +void pListView::setChecked(unsigned selection, bool checked) { } void pListView::setHeaderText(const lstring& text) { @@ -38,13 +24,16 @@ void pListView::setHeaderText(const lstring& text) { void pListView::setHeaderVisible(bool visible) { } -void pListView::setImage(unsigned row, unsigned column, const image& image) { +void pListView::setImage(unsigned selection, unsigned position, const image& image) { } void pListView::setSelected(bool selected) { } -void pListView::setSelection(unsigned row) { +void pListView::setSelection(unsigned selection) { +} + +void pListView::setText(unsigned selection, unsigned position, string text) { } void pListView::constructor() { diff --git a/phoenix/reference/widget/list-view.hpp b/phoenix/reference/widget/list-view.hpp index b9734a9f..21770157 100644 --- a/phoenix/reference/widget/list-view.hpp +++ b/phoenix/reference/widget/list-view.hpp @@ -5,19 +5,16 @@ struct pListView : public pWidget { void append(const lstring& text); void autoSizeColumns(); - bool checked(unsigned row); - void modify(unsigned row, const lstring& text); - void remove(unsigned row); + void remove(unsigned selection); void reset(); - bool selected(); - unsigned selection(); void setCheckable(bool checkable); - void setChecked(unsigned row, bool checked); + void setChecked(unsigned selection, bool checked); void setHeaderText(const lstring& text); void setHeaderVisible(bool visible); - void setImage(unsigned row, unsigned column, const image& image); + void setImage(unsigned selection, unsigned position, const image& image); void setSelected(bool selected); - void setSelection(unsigned row); + void setSelection(unsigned selection); + void setText(unsigned selection, unsigned position, string text); pListView(ListView& listView) : pWidget(listView), listView(listView) {} void constructor(); diff --git a/phoenix/reference/widget/radio-button.cpp b/phoenix/reference/widget/radio-button.cpp index 32695c0c..457499e6 100644 --- a/phoenix/reference/widget/radio-button.cpp +++ b/phoenix/reference/widget/radio-button.cpp @@ -1,15 +1,14 @@ namespace phoenix { -bool pRadioButton::checked() { - return false; -} - void pRadioButton::setChecked() { } void pRadioButton::setGroup(const group& group) { } +void pRadioButton::setImage(const image& image, Orientation orientation) { +} + void pRadioButton::setText(string text) { } diff --git a/phoenix/reference/widget/radio-button.hpp b/phoenix/reference/widget/radio-button.hpp index c1ac3ec9..d9377bc9 100644 --- a/phoenix/reference/widget/radio-button.hpp +++ b/phoenix/reference/widget/radio-button.hpp @@ -3,9 +3,9 @@ namespace phoenix { struct pRadioButton : public pWidget { RadioButton& radioButton; - bool checked(); void setChecked(); void setGroup(const group& group); + void setImage(const image& image, Orientation orientation); void setText(string text); pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {} diff --git a/phoenix/reference/widget/radio-label.cpp b/phoenix/reference/widget/radio-label.cpp new file mode 100644 index 00000000..61811514 --- /dev/null +++ b/phoenix/reference/widget/radio-label.cpp @@ -0,0 +1,18 @@ +namespace phoenix { + +void pRadioLabel::setChecked() { +} + +void pRadioLabel::setGroup(const group& group) { +} + +void pRadioLabel::setText(string text) { +} + +void pRadioLabel::constructor() { +} + +void pRadioLabel::destructor() { +} + +} diff --git a/phoenix/reference/widget/radio-label.hpp b/phoenix/reference/widget/radio-label.hpp new file mode 100644 index 00000000..ddd6b8a1 --- /dev/null +++ b/phoenix/reference/widget/radio-label.hpp @@ -0,0 +1,15 @@ +namespace phoenix { + +struct pRadioLabel : public pWidget { + RadioLabel& radioLabel; + + void setChecked(); + void setGroup(const group& group); + void setText(string text); + + pRadioLabel(RadioLabel& radioLabel) : pWidget(radioLabel), radioLabel(radioLabel) {} + void constructor(); + void destructor(); +}; + +} diff --git a/phoenix/reference/widget/tab-frame.cpp b/phoenix/reference/widget/tab-frame.cpp new file mode 100644 index 00000000..074a018a --- /dev/null +++ b/phoenix/reference/widget/tab-frame.cpp @@ -0,0 +1,24 @@ +namespace phoenix { + +void pTabFrame::append(string text, const image& image) { +} + +void pTabFrame::remove(unsigned selection) { +} + +void pTabFrame::setImage(unsigned selection, const image& image) { +} + +void pTabFrame::setSelection(unsigned selection) { +} + +void pTabFrame::setText(unsigned selection, string text) { +} + +void pTabFrame::constructor() { +} + +void pTabFrame::destructor() { +} + +} diff --git a/phoenix/reference/widget/tab-frame.hpp b/phoenix/reference/widget/tab-frame.hpp new file mode 100644 index 00000000..3efd9890 --- /dev/null +++ b/phoenix/reference/widget/tab-frame.hpp @@ -0,0 +1,17 @@ +namespace phoenix { + +struct pTabFrame : public pWidget { + TabFrame& tabFrame; + + void append(string text, const image& image); + void remove(unsigned selection); + void setImage(unsigned selection, const image& image); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); + + pTabFrame(TabFrame& tabFrame) : pWidget(tabFrame), tabFrame(tabFrame) {} + void constructor(); + void destructor(); +}; + +} diff --git a/phoenix/reference/widget/vertical-scroller.cpp b/phoenix/reference/widget/vertical-scroller.cpp index 866b0bd9..533653cb 100644 --- a/phoenix/reference/widget/vertical-scroller.cpp +++ b/phoenix/reference/widget/vertical-scroller.cpp @@ -1,9 +1,5 @@ namespace phoenix { -unsigned pVerticalScroller::position() { - return 0; -} - void pVerticalScroller::setLength(unsigned length) { } diff --git a/phoenix/reference/widget/vertical-scroller.hpp b/phoenix/reference/widget/vertical-scroller.hpp index 20353414..77e13a43 100644 --- a/phoenix/reference/widget/vertical-scroller.hpp +++ b/phoenix/reference/widget/vertical-scroller.hpp @@ -3,7 +3,6 @@ namespace phoenix { struct pVerticalScroller : public pWidget { VerticalScroller& verticalScroller; - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/reference/widget/vertical-slider.cpp b/phoenix/reference/widget/vertical-slider.cpp index 68ba9426..36d62eb8 100644 --- a/phoenix/reference/widget/vertical-slider.cpp +++ b/phoenix/reference/widget/vertical-slider.cpp @@ -1,9 +1,5 @@ namespace phoenix { -unsigned pVerticalSlider::position() { - return 0; -} - void pVerticalSlider::setLength(unsigned length) { } diff --git a/phoenix/reference/widget/vertical-slider.hpp b/phoenix/reference/widget/vertical-slider.hpp index 7039f783..0cfdf43b 100644 --- a/phoenix/reference/widget/vertical-slider.hpp +++ b/phoenix/reference/widget/vertical-slider.hpp @@ -3,7 +3,6 @@ namespace phoenix { struct pVerticalSlider : public pWidget { VerticalSlider& verticalSlider; - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); diff --git a/phoenix/reference/window.cpp b/phoenix/reference/window.cpp index f431e807..416f2151 100644 --- a/phoenix/reference/window.cpp +++ b/phoenix/reference/window.cpp @@ -15,10 +15,6 @@ void pWindow::append(Menu& menu) { void pWindow::append(Widget& widget) { } -Color pWindow::backgroundColor() { - return {0, 0, 0, 255}; -} - bool pWindow::focused() { return false; } diff --git a/phoenix/reference/window.hpp b/phoenix/reference/window.hpp index 7d3b6e3f..1039b9e6 100644 --- a/phoenix/reference/window.hpp +++ b/phoenix/reference/window.hpp @@ -8,7 +8,6 @@ struct pWindow : public pObject { void append(Layout& layout); void append(Menu& menu); void append(Widget& widget); - Color backgroundColor(); bool focused(); Geometry frameMargin(); Geometry geometry(); diff --git a/phoenix/windows/action/check-item.cpp b/phoenix/windows/action/check-item.cpp index 825ad702..cedea2b3 100644 --- a/phoenix/windows/action/check-item.cpp +++ b/phoenix/windows/action/check-item.cpp @@ -1,9 +1,5 @@ namespace phoenix { -bool pCheckItem::checked() { - return checkItem.state.checked; -} - void pCheckItem::setChecked(bool checked) { if(parentMenu) CheckMenuItem(parentMenu->p.hmenu, id, checked ? MF_CHECKED : MF_UNCHECKED); } @@ -19,4 +15,10 @@ void pCheckItem::destructor() { if(parentMenu) parentMenu->remove(checkItem); } +void pCheckItem::onToggle() { + checkItem.state.checked = !checkItem.state.checked; + setChecked(checkItem.state.checked); + if(checkItem.onToggle) checkItem.onToggle(); +} + } diff --git a/phoenix/windows/action/item.cpp b/phoenix/windows/action/item.cpp index a7a146e4..0f9ad74d 100644 --- a/phoenix/windows/action/item.cpp +++ b/phoenix/windows/action/item.cpp @@ -30,4 +30,8 @@ void pItem::createBitmap() { } } +void pItem::onActivate() { + if(item.onActivate) item.onActivate(); +} + } diff --git a/phoenix/windows/action/radio-item.cpp b/phoenix/windows/action/radio-item.cpp index ce0b445b..22f48134 100644 --- a/phoenix/windows/action/radio-item.cpp +++ b/phoenix/windows/action/radio-item.cpp @@ -1,9 +1,5 @@ namespace phoenix { -bool pRadioItem::checked() { - return radioItem.state.checked; -} - void pRadioItem::setChecked() { for(auto &item : radioItem.state.group) { //CheckMenuRadioItem takes: lo, hi, id; checking only id when lo <= id <= hi @@ -27,4 +23,10 @@ void pRadioItem::destructor() { if(parentMenu) parentMenu->remove(radioItem); } +void pRadioItem::onActivate() { + if(radioItem.state.checked) return; + radioItem.setChecked(); + if(radioItem.onActivate) radioItem.onActivate(); +} + } diff --git a/phoenix/windows/application.cpp b/phoenix/windows/application.cpp index a4625a85..0d346a15 100644 --- a/phoenix/windows/application.cpp +++ b/phoenix/windows/application.cpp @@ -187,290 +187,51 @@ static bool Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM return false; } +/*case WM_GETMINMAXINFO: { + MINMAXINFO* mmi = (MINMAXINFO*)lparam; + mmi->ptMinTrackSize.x = 256 + window.p.frameMargin().width; + mmi->ptMinTrackSize.y = 256 + window.p.frameMargin().height; + return TRUE; + break; + }*/ + static LRESULT CALLBACK Application_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { Object* object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if(!object || !dynamic_cast(object)) return DefWindowProc(hwnd, msg, wparam, lparam); - Window& window = (Window&)*object; + if(object == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam); + Window& window = dynamic_cast(object) ? (Window&)*object : *((Widget*)object)->Sizable::state.window; bool process = true; - if(pWindow::modal.size() > 0 && !pWindow::modal.find(&window.p)) process = false; + if(!pWindow::modal.empty() && !pWindow::modal.find(&window.p)) process = false; if(applicationState.quit) process = false; + if(process == false) return DefWindowProc(hwnd, msg, wparam, lparam); - if(process) switch(msg) { - case WM_CLOSE: { - if(window.onClose) window.onClose(); - else window.setVisible(false); - if(window.state.modal && !window.visible()) window.setModal(false); - return TRUE; - } - - case WM_MOVE: { - if(window.p.locked) break; - - Geometry geometry = window.geometry(); - window.state.geometry.x = geometry.x; - window.state.geometry.y = geometry.y; - - if(window.onMove) window.onMove(); - break; - } - - case WM_SIZE: { - if(window.p.locked) break; - SetWindowPos(window.p.hstatus, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED); - - Geometry geometry = window.geometry(); - window.state.geometry.width = geometry.width; - window.state.geometry.height = geometry.height; - - for(auto& layout : window.state.layout) { - Geometry geom = window.geometry(); - geom.x = geom.y = 0; - layout.setGeometry(geom); - } - - if(window.onSize) window.onSize(); - break; - } - - case WM_GETMINMAXINFO: { - MINMAXINFO* mmi = (MINMAXINFO*)lparam; - //mmi->ptMinTrackSize.x = 256 + window.p.frameMargin().width; - //mmi->ptMinTrackSize.y = 256 + window.p.frameMargin().height; - //return TRUE; - break; - } - - case WM_ENTERMENULOOP: - case WM_ENTERSIZEMOVE: { - if(Application::Windows::onModalBegin) Application::Windows::onModalBegin(); - return 0; - } - - case WM_EXITMENULOOP: - case WM_EXITSIZEMOVE: { - if(Application::Windows::onModalEnd) Application::Windows::onModalEnd(); - return 0; - } - - case WM_ERASEBKGND: { - if(window.p.brush == 0) break; - RECT rc; - GetClientRect(window.p.hwnd, &rc); - PAINTSTRUCT ps; - BeginPaint(window.p.hwnd, &ps); - FillRect(ps.hdc, &rc, window.p.brush); - EndPaint(window.p.hwnd, &ps); - return TRUE; - } - - case WM_CTLCOLORBTN: - case WM_CTLCOLORSTATIC: { - Object* object = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA); - if(object == nullptr) break; - if(dynamic_cast(object) || dynamic_cast(object) || dynamic_cast(object)) { - //text edit controls, when disabled, use CTLCOLORSTATIC instead of CTLCOLOREDIT - //override this behavior: we do not want read-only edit controls to use the parent window background color - return DefWindowProc(hwnd, WM_CTLCOLOREDIT, wparam, lparam); - } else if(window.p.brush) { - HDC hdc = (HDC)wparam; - SetBkColor((HDC)wparam, window.p.brushColor); - return (INT_PTR)window.p.brush; - } - break; - } - - case WM_DROPFILES: { - lstring paths = DropPaths(wparam); - if(paths.empty() == false) { - if(window.onDrop) window.onDrop(paths); - } - return FALSE; - } - - case WM_COMMAND: { - unsigned id = LOWORD(wparam); - HWND control = GetDlgItem(window.p.hwnd, id); - if(control == 0) { - pObject* object = (pObject*)pObject::find(id); - if(!object) break; - if(dynamic_cast(object)) { - Item& item = ((pItem*)object)->item; - if(item.onActivate) item.onActivate(); - } else if(dynamic_cast(object)) { - CheckItem& checkItem = ((pCheckItem*)object)->checkItem; - checkItem.setChecked(!checkItem.state.checked); - if(checkItem.onToggle) checkItem.onToggle(); - } else if(dynamic_cast(object)) { - RadioItem& radioItem = ((pRadioItem*)object)->radioItem; - if(radioItem.state.checked == false) { - radioItem.setChecked(); - if(radioItem.onActivate) radioItem.onActivate(); - } - } - } else { - Object* object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); - if(!object) break; - if(dynamic_cast(object)) { - Button& button = (Button&)*object; - if(button.onActivate) button.onActivate(); - } else if(dynamic_cast(object)) { - CheckButton& checkButton = (CheckButton&)*object; - checkButton.setChecked(!checkButton.state.checked); - if(checkButton.onToggle) checkButton.onToggle(); - } else if(dynamic_cast(object)) { - ComboButton& comboButton = (ComboButton&)*object; - if(HIWORD(wparam) == CBN_SELCHANGE) { - if(comboButton.state.selection != comboButton.selection()) { - comboButton.state.selection = comboButton.selection(); - if(comboButton.onChange) comboButton.onChange(); - } - } - } else if(dynamic_cast(object)) { - LineEdit& lineEdit = (LineEdit&)*object; - if(HIWORD(wparam) == EN_CHANGE) { - if(lineEdit.p.locked == false && lineEdit.onChange) lineEdit.onChange(); - } - } else if(dynamic_cast(object)) { - RadioButton& radioButton = (RadioButton&)*object; - if(radioButton.state.checked == false) { - radioButton.setChecked(); - if(radioButton.onActivate) radioButton.onActivate(); - } - } else if(dynamic_cast(object)) { - TextEdit& textEdit = (TextEdit&)*object; - if(HIWORD(wparam) == EN_CHANGE) { - if(textEdit.p.locked == false && textEdit.onChange) textEdit.onChange(); - } - } - } - break; - } - - case WM_NOTIFY: { - unsigned id = LOWORD(wparam); - HWND control = GetDlgItem(window.p.hwnd, id); - if(control == 0) break; - Object* object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); - if(object == nullptr) break; - if(dynamic_cast(object)) { - ListView& listView = (ListView&)*object; - LPNMHDR nmhdr = (LPNMHDR)lparam; - LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam; - - if(nmhdr->code == LVN_ITEMCHANGED && (nmlistview->uChanged & LVIF_STATE)) { - unsigned imagemask = ((nmlistview->uNewState & LVIS_STATEIMAGEMASK) >> 12) - 1; - if(imagemask == 0 || imagemask == 1) { - if(listView.p.locked == false && listView.onToggle) listView.onToggle(nmlistview->iItem); - } else if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) { - listView.p.lostFocus = true; - } else if(!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED)) { - listView.p.lostFocus = false; - listView.state.selected = true; - listView.state.selection = listView.selection(); - if(listView.p.locked == false && listView.onChange) listView.onChange(); - } else if(listView.p.lostFocus == false && listView.selected() == false) { - listView.p.lostFocus = false; - listView.state.selected = false; - listView.state.selection = 0; - if(listView.p.locked == false && listView.onChange) listView.onChange(); - } - } else if(nmhdr->code == LVN_ITEMACTIVATE) { - if(listView.state.text.size() && listView.selected()) { - if(listView.onActivate) listView.onActivate(); - } - } else if(nmhdr->code == NM_CUSTOMDRAW) { - LPNMLVCUSTOMDRAW lvcd = (LPNMLVCUSTOMDRAW)nmhdr; - switch(lvcd->nmcd.dwDrawStage) { - case CDDS_PREPAINT: - return CDRF_NOTIFYITEMDRAW; - case CDDS_ITEMPREPAINT: - if(listView.state.headerText.size() >= 2) { - //draw alternating row colors of there are two or more columns - if(lvcd->nmcd.dwItemSpec % 2) lvcd->clrTextBk = GetSysColor(COLOR_WINDOW) ^ 0x070707; - } - return CDRF_DODEFAULT; - default: - return CDRF_DODEFAULT; - } - } - } - break; - } - - case WM_HSCROLL: - case WM_VSCROLL: { - Object* object = nullptr; - if(lparam) { - object = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA); - } else { - unsigned id = LOWORD(wparam); - HWND control = GetDlgItem(window.p.hwnd, id); - if(control == 0) break; - object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); - } - if(object == nullptr) break; - - if(dynamic_cast(object) - || dynamic_cast(object)) { - SCROLLINFO info; - memset(&info, 0, sizeof(SCROLLINFO)); - info.cbSize = sizeof(SCROLLINFO); - info.fMask = SIF_ALL; - GetScrollInfo((HWND)lparam, SB_CTL, &info); - - switch(LOWORD(wparam)) { - case SB_LEFT: info.nPos = info.nMin; break; - case SB_RIGHT: info.nPos = info.nMax; break; - case SB_LINELEFT: info.nPos--; break; - case SB_LINERIGHT: info.nPos++; break; - case SB_PAGELEFT: info.nPos -= info.nMax >> 3; break; - case SB_PAGERIGHT: info.nPos += info.nMax >> 3; break; - case SB_THUMBTRACK: info.nPos = info.nTrackPos; break; - } - - info.fMask = SIF_POS; - SetScrollInfo((HWND)lparam, SB_CTL, &info, TRUE); - - //Windows may clamp position to scroller range - GetScrollInfo((HWND)lparam, SB_CTL, &info); - - if(dynamic_cast(object)) { - HorizontalScroller& horizontalScroller = (HorizontalScroller&)*object; - if(horizontalScroller.state.position != info.nPos) { - horizontalScroller.state.position = info.nPos; - if(horizontalScroller.onChange) horizontalScroller.onChange(); - } - } else { - VerticalScroller& verticalScroller = (VerticalScroller&)*object; - if(verticalScroller.state.position != info.nPos) { - verticalScroller.state.position = info.nPos; - if(verticalScroller.onChange) verticalScroller.onChange(); - } - } - - return TRUE; - } - - if(dynamic_cast(object)) { - HorizontalSlider& horizontalSlider = (HorizontalSlider&)*object; - if(horizontalSlider.state.position != horizontalSlider.position()) { - horizontalSlider.state.position = horizontalSlider.position(); - if(horizontalSlider.onChange) horizontalSlider.onChange(); - } - } else if(dynamic_cast(object)) { - VerticalSlider& verticalSlider = (VerticalSlider&)*object; - if(verticalSlider.state.position != verticalSlider.position()) { - verticalSlider.state.position = verticalSlider.position(); - if(verticalSlider.onChange) verticalSlider.onChange(); - } - } - - break; + switch(msg) { + case WM_CLOSE: window.p.onClose(); return TRUE; + case WM_MOVE: window.p.onMove(); break; + case WM_SIZE: window.p.onSize(); break; + case WM_DROPFILES: window.p.onDrop(wparam); return FALSE; + case WM_ERASEBKGND: if(window.p.onEraseBackground()) return true; break; + case WM_ENTERMENULOOP: case WM_ENTERSIZEMOVE: window.p.onModalBegin(); return FALSE; + case WM_EXITMENULOOP: case WM_EXITSIZEMOVE: window.p.onModalEnd(); return FALSE; + + case WM_CTLCOLORBTN: + case WM_CTLCOLORSTATIC: { + Object* object = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA); + if(object == nullptr) break; + if(dynamic_cast(object) || dynamic_cast(object) || dynamic_cast(object)) { + //text edit controls, when disabled, use CTLCOLORSTATIC instead of CTLCOLOREDIT + //override this behavior: we do not want read-only edit controls to use the parent window background color + return DefWindowProc(hwnd, WM_CTLCOLOREDIT, wparam, lparam); + } else if(window.p.brush) { + HDC hdc = (HDC)wparam; + SetBkColor((HDC)wparam, window.p.brushColor); + return (INT_PTR)window.p.brush; } + break; + } } - return DefWindowProc(hwnd, msg, wparam, lparam); + return Shared_windowProc(DefWindowProc, hwnd, msg, wparam, lparam); } } diff --git a/phoenix/windows/browser-window.cpp b/phoenix/windows/browser-window.cpp index c8c6cdd7..fd60d1ff 100644 --- a/phoenix/windows/browser-window.cpp +++ b/phoenix/windows/browser-window.cpp @@ -13,7 +13,7 @@ static int CALLBACK BrowserWindowCallbackProc(HWND hwnd, UINT msg, LPARAM lparam } static string BrowserWindow_fileDialog(bool save, BrowserWindow::State& state) { - string path = string{path}.replace("/", "\\"); + string path = string{state.path}.replace("/", "\\"); string filters; for(auto& filter : state.filters) { diff --git a/phoenix/windows/header.hpp b/phoenix/windows/header.hpp index fb15f5d9..d561f87a 100644 --- a/phoenix/windows/header.hpp +++ b/phoenix/windows/header.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/phoenix/windows/platform.cpp b/phoenix/windows/platform.cpp index 56386e68..5fc455bf 100644 --- a/phoenix/windows/platform.cpp +++ b/phoenix/windows/platform.cpp @@ -24,8 +24,10 @@ #include "widget/button.cpp" #include "widget/canvas.cpp" #include "widget/check-button.cpp" +#include "widget/check-label.cpp" #include "widget/combo-button.cpp" #include "widget/console.cpp" +#include "widget/frame.cpp" #include "widget/hex-edit.cpp" #include "widget/horizontal-scroller.cpp" #include "widget/horizontal-slider.cpp" @@ -34,6 +36,8 @@ #include "widget/list-view.cpp" #include "widget/progress-bar.cpp" #include "widget/radio-button.cpp" +#include "widget/radio-label.cpp" +#include "widget/tab-frame.cpp" #include "widget/text-edit.cpp" #include "widget/vertical-scroller.cpp" #include "widget/vertical-slider.cpp" diff --git a/phoenix/windows/platform.hpp b/phoenix/windows/platform.hpp index bc68a2cd..0cc2a8fc 100644 --- a/phoenix/windows/platform.hpp +++ b/phoenix/windows/platform.hpp @@ -1,5 +1,7 @@ namespace phoenix { +typedef LRESULT CALLBACK (*WindowProc)(HWND, UINT, WPARAM, LPARAM); + struct pApplication { static void run(); static bool pendingEvents(); @@ -81,7 +83,7 @@ struct pTimer : public pObject { UINT_PTR htimer; void setEnabled(bool enabled); - void setInterval(unsigned milliseconds); + void setInterval(unsigned interval); pTimer(Timer& timer) : pObject(timer), timer(timer) {} void constructor(); @@ -104,7 +106,6 @@ struct pWindow : public pObject { void append(Layout& layout); void append(Menu& menu); void append(Widget& widget); - Color backgroundColor(); bool focused(); Geometry frameMargin(); Geometry geometry(); @@ -131,6 +132,14 @@ struct pWindow : public pObject { void constructor(); void destructor(); void updateMenu(); + + void onClose(); + void onDrop(WPARAM wparam); + bool onEraseBackground(); + void onModalBegin(); + void onModalEnd(); + void onMove(); + void onSize(); }; struct pAction : public pObject { @@ -181,24 +190,26 @@ struct pItem : public pAction { void constructor(); void destructor(); void createBitmap(); + + void onActivate(); }; struct pCheckItem : public pAction { CheckItem& checkItem; - bool checked(); void setChecked(bool checked); void setText(string text); pCheckItem(CheckItem& checkItem) : pAction(checkItem), checkItem(checkItem) {} void constructor(); void destructor(); + + void onToggle(); }; struct pRadioItem : public pAction { RadioItem& radioItem; - bool checked(); void setChecked(); void setGroup(const group& group); void setText(string text); @@ -206,6 +217,8 @@ struct pRadioItem : public pAction { pRadioItem(RadioItem& radioItem) : pAction(radioItem), radioItem(radioItem) {} void constructor(); void destructor(); + + void onActivate(); }; struct pSizable : public pObject { @@ -222,20 +235,19 @@ struct pLayout : public pSizable { struct pWidget : public pSizable { Widget& widget; - Window* parentWindow; + HWND parentHwnd; HWND hwnd; HFONT hfont; - bool enabled(); bool focused(); virtual Size minimumSize(); - void setEnabled(bool enabled); + virtual void setEnabled(bool enabled); void setFocused(); void setFont(string font); virtual void setGeometry(Geometry geometry); - void setVisible(bool visible); + virtual void setVisible(bool visible); - pWidget(Widget& widget) : pSizable(widget), widget(widget) { parentWindow = &Window::none(); } + pWidget(Widget& widget) : pSizable(widget), widget(widget) { parentHwnd = pWindow::none().p.hwnd; } void constructor(); void destructor(); virtual void orphan(); @@ -256,53 +268,81 @@ struct pButton : public pWidget { void constructor(); void destructor(); void orphan(); + + void onActivate(); }; struct pCanvas : public pWidget { Canvas& canvas; - uint32_t* data; + uint32_t* surface = nullptr; + unsigned surfaceWidth = 0; + unsigned surfaceHeight = 0; void setDroppable(bool droppable); + void setGeometry(Geometry geometry); + void setMode(Canvas::Mode mode); void setSize(Size size); - void update(); pCanvas(Canvas& canvas) : pWidget(canvas), canvas(canvas) {} void constructor(); void destructor(); void orphan(); void paint(); + void rasterize(); + void redraw(); + void release(); }; struct pCheckButton : public pWidget { CheckButton& checkButton; + HBITMAP hbitmap; + HIMAGELIST himagelist; - bool checked(); Size minimumSize(); void setChecked(bool checked); + void setImage(const image& image, Orientation orientation); void setText(string text); pCheckButton(CheckButton& checkButton) : pWidget(checkButton), checkButton(checkButton) {} void constructor(); void destructor(); void orphan(); + + void onToggle(); +}; + +struct pCheckLabel : public pWidget { + CheckLabel& checkLabel; + + Size minimumSize(); + void setChecked(bool checked); + void setText(string text); + + pCheckLabel(CheckLabel& checkLabel) : pWidget(checkLabel), checkLabel(checkLabel) {} + void constructor(); + void destructor(); + void orphan(); + + void onToggle(); }; struct pComboButton : public pWidget { ComboButton& comboButton; void append(string text); - void modify(unsigned row, string text); - void remove(unsigned row); + void remove(unsigned selection); Size minimumSize(); void reset(); - unsigned selection(); - void setSelection(unsigned row); + void setGeometry(Geometry geometry); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); pComboButton(ComboButton& comboButton) : pWidget(comboButton), comboButton(comboButton) {} void constructor(); void destructor(); void orphan(); - void setGeometry(Geometry geometry); + + void onChange(); }; struct pConsole : public pWidget { @@ -319,6 +359,20 @@ struct pConsole : public pWidget { bool keyPress(unsigned key); }; +struct pFrame : public pWidget { + Frame& frame; + + void setEnabled(bool enabled); + void setGeometry(Geometry geometry); + void setText(string text); + void setVisible(bool visible); + + pFrame(Frame& frame) : pWidget(frame), frame(frame) {} + void constructor(); + void destructor(); + void orphan(); +}; + struct pHexEdit : public pWidget { HexEdit& hexEdit; HWND scrollBar; @@ -345,7 +399,6 @@ struct pHorizontalScroller : public pWidget { HorizontalScroller& horizontalScroller; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -353,13 +406,14 @@ struct pHorizontalScroller : public pWidget { void constructor(); void destructor(); void orphan(); + + void onChange(WPARAM wparam); }; struct pHorizontalSlider : public pWidget { HorizontalSlider& horizontalSlider; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -367,6 +421,8 @@ struct pHorizontalSlider : public pWidget { void constructor(); void destructor(); void orphan(); + + void onChange(); }; struct pLabel : public pWidget { @@ -393,6 +449,8 @@ struct pLineEdit : public pWidget { void constructor(); void destructor(); void orphan(); + + void onChange(); }; struct pListView : public pWidget { @@ -404,26 +462,27 @@ struct pListView : public pWidget { void append(const lstring& text); void autoSizeColumns(); - bool checked(unsigned row); - void modify(unsigned row, const lstring& text); - void remove(unsigned row); + void remove(unsigned selection); void reset(); - bool selected(); - unsigned selection(); void setCheckable(bool checkable); - void setChecked(unsigned row, bool checked); + void setChecked(unsigned selection, bool checked); + void setGeometry(Geometry geometry); void setHeaderText(const lstring& text); void setHeaderVisible(bool visible); - void setImage(unsigned row, unsigned column, const image& image); + void setImage(unsigned selection, unsigned position, const image& image); void setSelected(bool selected); - void setSelection(unsigned row); + void setSelection(unsigned selection); + void setText(unsigned selection, unsigned position, string text); pListView(ListView& listView) : pWidget(listView), listView(listView), imageList(nullptr) {} void constructor(); void destructor(); void orphan(); - void setGeometry(Geometry geometry); void buildImageList(); + + void onActivate(LPARAM lparam); + void onChange(LPARAM lparam); + LRESULT onCustomDraw(LPARAM lparam); }; struct pProgressBar : public pWidget { @@ -440,17 +499,62 @@ struct pProgressBar : public pWidget { struct pRadioButton : public pWidget { RadioButton& radioButton; + HBITMAP hbitmap; + HIMAGELIST himagelist; - bool checked(); Size minimumSize(); void setChecked(); void setGroup(const group& group); + void setImage(const image& image, Orientation orientation); void setText(string text); pRadioButton(RadioButton& radioButton) : pWidget(radioButton), radioButton(radioButton) {} void constructor(); void destructor(); void orphan(); + + void onActivate(); +}; + +struct pRadioLabel : public pWidget { + RadioLabel& radioLabel; + + Size minimumSize(); + void setChecked(); + void setGroup(const group& group); + void setText(string text); + + pRadioLabel(RadioLabel& radioLabel) : pWidget(radioLabel), radioLabel(radioLabel) {} + void constructor(); + void destructor(); + void orphan(); + + void onActivate(); +}; + +struct pTabFrame : public pWidget { + TabFrame& tabFrame; + WindowProc windowProc = nullptr; + HIMAGELIST imageList = nullptr; + + void append(string text, const image& image); + void remove(unsigned selection); + void setEnabled(bool enabled); + void setGeometry(Geometry geometry); + void setImage(unsigned selection, const image& image); + void setSelection(unsigned selection); + void setText(unsigned selection, string text); + void setVisible(bool visible); + + pTabFrame(TabFrame& tabFrame) : pWidget(tabFrame), tabFrame(tabFrame) {} + void constructor(); + void destructor(); + void orphan(); + void buildImageList(); + void synchronizeLayout(); + + void onChange(); + void onDrawItem(LPARAM lparam); }; struct pTextEdit : public pWidget { @@ -466,13 +570,14 @@ struct pTextEdit : public pWidget { void constructor(); void destructor(); void orphan(); + + void onChange(); }; struct pVerticalScroller : public pWidget { VerticalScroller& verticalScroller; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -480,13 +585,14 @@ struct pVerticalScroller : public pWidget { void constructor(); void destructor(); void orphan(); + + void onChange(WPARAM wparam); }; struct pVerticalSlider : public pWidget { VerticalSlider& verticalSlider; Size minimumSize(); - unsigned position(); void setLength(unsigned length); void setPosition(unsigned position); @@ -494,6 +600,8 @@ struct pVerticalSlider : public pWidget { void constructor(); void destructor(); void orphan(); + + void onChange(); }; struct pViewport : public pWidget { diff --git a/phoenix/windows/timer.cpp b/phoenix/windows/timer.cpp index 4f579c33..8c209b2f 100644 --- a/phoenix/windows/timer.cpp +++ b/phoenix/windows/timer.cpp @@ -18,11 +18,11 @@ void pTimer::setEnabled(bool enabled) { } if(enabled == true) { - htimer = SetTimer(NULL, 0U, timer.state.milliseconds, Timer_timeoutProc); + htimer = SetTimer(NULL, 0u, timer.state.interval, Timer_timeoutProc); } } -void pTimer::setInterval(unsigned milliseconds) { +void pTimer::setInterval(unsigned interval) { //destroy and recreate timer if interval changed setEnabled(timer.state.enabled); } diff --git a/phoenix/windows/utility.cpp b/phoenix/windows/utility.cpp index a1375539..f7f76de2 100644 --- a/phoenix/windows/utility.cpp +++ b/phoenix/windows/utility.cpp @@ -30,12 +30,6 @@ static HBITMAP CreateBitmap(const image& image) { return hbitmap; } -static unsigned GetWindowZOrder(HWND hwnd) { - unsigned z = 0; - for(HWND next = hwnd; next != NULL; next = GetWindow(next, GW_HWNDPREV)) z++; - return z; -} - static lstring DropPaths(WPARAM wparam) { auto dropList = HDROP(wparam); auto fileCount = DragQueryFile(dropList, ~0u, nullptr, 0); @@ -58,6 +52,28 @@ static lstring DropPaths(WPARAM wparam) { return paths; } +static Layout* GetParentWidgetLayout(Sizable* sizable) { + while(sizable) { + if(sizable->state.parent && dynamic_cast(sizable->state.parent)) return (Layout*)sizable; + sizable = sizable->state.parent; + } + return nullptr; +} + +static Widget* GetParentWidget(Sizable* sizable) { + while(sizable) { + if(sizable->state.parent && dynamic_cast(sizable->state.parent)) return (Widget*)sizable->state.parent; + sizable = sizable->state.parent; + } + return nullptr; +} + +static unsigned GetWindowZOrder(HWND hwnd) { + unsigned z = 0; + for(HWND next = hwnd; next != NULL; next = GetWindow(next, GW_HWNDPREV)) z++; + return z; +} + static Keyboard::Keycode Keysym(unsigned keysym, unsigned keyflags) { #define pressed(keysym) (GetAsyncKeyState(keysym) & 0x8000) #define enabled(keysym) (GetKeyState(keysym)) @@ -179,4 +195,101 @@ static Keyboard::Keycode Keysym(unsigned keysym, unsigned keyflags) { #undef extended } +static unsigned ScrollEvent(HWND hwnd, WPARAM wparam) { + SCROLLINFO info; + memset(&info, 0, sizeof(SCROLLINFO)); + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_ALL; + GetScrollInfo(hwnd, SB_CTL, &info); + + switch(LOWORD(wparam)) { + case SB_LEFT: info.nPos = info.nMin; break; + case SB_RIGHT: info.nPos = info.nMax; break; + case SB_LINELEFT: info.nPos--; break; + case SB_LINERIGHT: info.nPos++; break; + case SB_PAGELEFT: info.nPos -= info.nMax >> 3; break; + case SB_PAGERIGHT: info.nPos += info.nMax >> 3; break; + case SB_THUMBTRACK: info.nPos = info.nTrackPos; break; + } + + info.fMask = SIF_POS; + SetScrollInfo(hwnd, SB_CTL, &info, TRUE); + + //Windows may clamp position to scroller range + GetScrollInfo(hwnd, SB_CTL, &info); + return info.nPos; +} + +static LRESULT CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + Object* object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(object == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam); + Window& window = dynamic_cast(object) ? *(Window*)object : *((Widget*)object)->Sizable::state.window; + + bool process = true; + if(!pWindow::modal.empty() && !pWindow::modal.find(&window.p)) process = false; + if(applicationState.quit) process = false; + if(process == false) return DefWindowProc(hwnd, msg, wparam, lparam); + + switch(msg) { + case WM_DRAWITEM: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(hwnd, id); + Object* object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object == nullptr) break; + if(dynamic_cast(object)) { ((TabFrame*)object)->p.onDrawItem(lparam); break; } + break; + } + + case WM_COMMAND: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(hwnd, id); + Object* object = control ? (Object*)GetWindowLongPtr(control, GWLP_USERDATA) : (Object*)(&pObject::find(id)->object); + if(object == nullptr) break; + if(dynamic_cast(object)) { ((Item*)object)->p.onActivate(); break; } + if(dynamic_cast(object)) { ((CheckItem*)object)->p.onToggle(); break; } + if(dynamic_cast(object)) { ((Button*)object)->p.onActivate(); break; } + if(dynamic_cast(object)) { ((CheckButton*)object)->p.onToggle(); break; } + if(dynamic_cast(object)) { ((CheckLabel*)object)->p.onToggle(); break; } + if(dynamic_cast(object) && HIWORD(wparam) == CBN_SELCHANGE) { ((ComboButton*)object)->p.onChange(); break; } + if(dynamic_cast(object) && HIWORD(wparam) == EN_CHANGE) { ((LineEdit*)object)->p.onChange(); break; } + if(dynamic_cast(object)) { ((RadioButton*)object)->p.onActivate(); break; } + if(dynamic_cast(object)) { ((RadioLabel*)object)->p.onActivate(); break; } + if(dynamic_cast(object) && HIWORD(wparam) == EN_CHANGE) { ((TextEdit*)object)->p.onChange(); break; } + break; + } + + case WM_NOTIFY: { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(hwnd, id); + Object* object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + if(object == nullptr) break; + if(dynamic_cast(object) && ((LPNMHDR)lparam)->code == LVN_ITEMACTIVATE) { ((ListView*)object)->p.onActivate(lparam); break; } + if(dynamic_cast(object) && ((LPNMHDR)lparam)->code == LVN_ITEMCHANGED) { ((ListView*)object)->p.onChange(lparam); break; } + if(dynamic_cast(object) && ((LPNMHDR)lparam)->code == NM_CUSTOMDRAW) { return ((ListView*)object)->p.onCustomDraw(lparam); } + if(dynamic_cast(object) && ((LPNMHDR)lparam)->code == TCN_SELCHANGE) { ((TabFrame*)object)->p.onChange(); break; } + break; + } + + case WM_HSCROLL: + case WM_VSCROLL: { + Object* object = nullptr; + if(lparam) { + object = (Object*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA); + } else { + unsigned id = LOWORD(wparam); + HWND control = GetDlgItem(hwnd, id); + object = (Object*)GetWindowLongPtr(control, GWLP_USERDATA); + } + if(object == nullptr) break; + if(dynamic_cast(object)) { ((HorizontalScroller*)object)->p.onChange(wparam); return TRUE; } + if(dynamic_cast(object)) { ((VerticalScroller*)object)->p.onChange(wparam); return TRUE; } + if(dynamic_cast(object)) { ((HorizontalSlider*)object)->p.onChange(); break; } + if(dynamic_cast(object)) { ((VerticalSlider*)object)->p.onChange(); break; } + break; + } + } + + return windowProc(hwnd, msg, wparam, lparam); +} + } diff --git a/phoenix/windows/widget/button.cpp b/phoenix/windows/widget/button.cpp index dd861176..0598c620 100644 --- a/phoenix/windows/widget/button.cpp +++ b/phoenix/windows/widget/button.cpp @@ -42,29 +42,19 @@ void pButton::setImage(const image& image, Orientation orientation) { if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; } - if(OsVersion() >= WindowsVista) { - hbitmap = CreateBitmap(nallImage); - SendMessage(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbitmap); - switch(orientation) { - case Orientation::Horizontal: SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~BS_TOP); break; - case Orientation::Vertical: SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_TOP); break; - } - } else { - //Windows XP and earlier cannot display bitmaps and text at the same time with BM_SETIMAGE - //Use BCM_SETIMAGELIST instead. It does not support alpha blending, so blend with button color - //The XP theme and above use a gradient fade background, so it won't be a perfect match there - nallImage.alphaBlend(GetSysColor(COLOR_BTNFACE)); - hbitmap = CreateBitmap(nallImage); - himagelist = ImageList_Create(nallImage.width, nallImage.height, ILC_COLOR32, 1, 0); - ImageList_Add(himagelist, hbitmap, NULL); - BUTTON_IMAGELIST list; - list.himl = himagelist; - switch(orientation) { - case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break; - case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break; - } - Button_SetImageList(hwnd, &list); + //Windows XP and earlier do not support translucent images + //so we must blend with the button background color (which does not look great with XP gradient-button themes) + if(OsVersion() < WindowsVista) nallImage.alphaBlend(GetSysColor(COLOR_BTNFACE)); + hbitmap = CreateBitmap(nallImage); + himagelist = ImageList_Create(nallImage.width, nallImage.height, ILC_COLOR32, 1, 0); + ImageList_Add(himagelist, hbitmap, NULL); + BUTTON_IMAGELIST list; + list.himl = himagelist; + switch(orientation) { + case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break; + case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break; } + Button_SetImageList(hwnd, &list); setText(button.state.text); //update text to display nicely with image (or lack thereof) } @@ -78,16 +68,11 @@ void pButton::setText(string text) { SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~BS_BITMAP); } - if(OsVersion() >= WindowsVista && button.state.image.empty() == false && text.empty() == false) { - //Vista+ does not add spacing between the icon and text; causing them to run into each other - SetWindowText(hwnd, utf16_t(string{" ", text})); - } else { - SetWindowText(hwnd, utf16_t(text)); - } + SetWindowText(hwnd, utf16_t(text)); } void pButton::constructor() { - hwnd = CreateWindow(L"BUTTON", L"", WS_CHILD | WS_TABSTOP, 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0); + hwnd = CreateWindow(L"BUTTON", L"", WS_CHILD | WS_TABSTOP, 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&button); setDefaultFont(); setImage(button.state.image, button.state.orientation); @@ -106,4 +91,8 @@ void pButton::orphan() { constructor(); } +void pButton::onActivate() { + if(button.onActivate) button.onActivate(); +} + } diff --git a/phoenix/windows/widget/canvas.cpp b/phoenix/windows/widget/canvas.cpp index 887650c5..9e0dc815 100644 --- a/phoenix/windows/widget/canvas.cpp +++ b/phoenix/windows/widget/canvas.cpp @@ -18,6 +18,11 @@ static LRESULT CALLBACK Canvas_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LP return DLGC_STATIC | DLGC_WANTCHARS; } + if(msg == WM_ERASEBKGND) { + //background is erased during WM_PAINT to prevent flickering + return TRUE; + } + if(msg == WM_PAINT) { canvas.p.paint(); return TRUE; @@ -56,28 +61,45 @@ void pCanvas::setDroppable(bool droppable) { DragAcceptFiles(hwnd, droppable); } -void pCanvas::setSize(Size size) { - delete[] data; - data = new uint32_t[size.width * size.height]; +void pCanvas::setGeometry(Geometry geometry) { + if(canvas.state.width == 0 || canvas.state.height == 0) rasterize(); + unsigned width = canvas.state.width; + unsigned height = canvas.state.height; + if(width == 0) width = widget.state.geometry.width; + if(height == 0) height = widget.state.geometry.height; + + if(width < geometry.width) { + geometry.x += (geometry.width - width) / 2; + geometry.width = width; + } + + if(height < geometry.height) { + geometry.y += (geometry.height - height) / 2; + geometry.height = height; + } + + pWidget::setGeometry(geometry); } -void pCanvas::update() { - memcpy(data, canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t)); - InvalidateRect(hwnd, 0, false); +void pCanvas::setMode(Canvas::Mode mode) { + rasterize(), redraw(); +} + +void pCanvas::setSize(Size size) { + rasterize(), redraw(); } void pCanvas::constructor() { - data = new uint32_t[canvas.state.width * canvas.state.height]; - memcpy(data, canvas.state.data, canvas.state.width * canvas.state.height * sizeof(uint32_t)); - hwnd = CreateWindow(L"phoenix_canvas", L"", WS_CHILD, 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0); + hwnd = CreateWindow(L"phoenix_canvas", L"", WS_CHILD, 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&canvas); setDroppable(canvas.state.droppable); + rasterize(); synchronize(); } void pCanvas::destructor() { + release(); DestroyWindow(hwnd); - delete[] data; } void pCanvas::orphan() { @@ -86,10 +108,16 @@ void pCanvas::orphan() { } void pCanvas::paint() { - RECT rc; - GetClientRect(hwnd, &rc); - unsigned width = canvas.state.width, height = canvas.state.height; + if(surface == nullptr) return; + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + + uint32_t* data = surface; + unsigned width = surfaceWidth; + unsigned height = surfaceHeight; + + HDC hdc = CreateCompatibleDC(ps.hdc); BITMAPINFO bmi; memset(&bmi, 0, sizeof(BITMAPINFO)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); @@ -98,12 +126,84 @@ void pCanvas::paint() { bmi.bmiHeader.biCompression = BI_RGB; bmi.bmiHeader.biWidth = width; bmi.bmiHeader.biHeight = -height; //GDI stores bitmaps upside now; negative height flips bitmap - bmi.bmiHeader.biSizeImage = sizeof(uint32_t) * width * height; + bmi.bmiHeader.biSizeImage = width * height * sizeof(uint32_t); + void* bits = nullptr; + HBITMAP bitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0); + if(bits) memcpy(bits, data, width * height * sizeof(uint32_t)); + SelectObject(hdc, bitmap); + + BLENDFUNCTION bf; + bf.BlendOp = AC_SRC_OVER; + bf.BlendFlags = 0; + bf.SourceConstantAlpha = 255; + bf.AlphaFormat = AC_SRC_ALPHA; + + RECT rc; + GetClientRect(hwnd, &rc); + DrawThemeParentBackground(hwnd, ps.hdc, &rc); + AlphaBlend(ps.hdc, 0, 0, width, height, hdc, 0, 0, width, height, bf); + + DeleteObject(bitmap); + DeleteDC(hdc); - PAINTSTRUCT ps; - BeginPaint(hwnd, &ps); - SetDIBitsToDevice(ps.hdc, 0, 0, width, height, 0, 0, 0, height, (void*)data, &bmi, DIB_RGB_COLORS); EndPaint(hwnd, &ps); } +void pCanvas::rasterize() { + unsigned width = canvas.state.width; + unsigned height = canvas.state.height; + if(width == 0) width = widget.state.geometry.width; + if(height == 0) height = widget.state.geometry.height; + + if(width != surfaceWidth || height != surfaceHeight) release(); + if(!surface) surface = new uint32_t[width * height]; + + if(canvas.state.mode == Canvas::Mode::Color) { + nall::image image; + image.allocate(width, height); + image.fill(canvas.state.color.argb()); + memcpy(surface, image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Gradient) { + nall::image image; + image.allocate(width, height); + image.gradient( + canvas.state.gradient[0].argb(), canvas.state.gradient[1].argb(), canvas.state.gradient[2].argb(), canvas.state.gradient[3].argb() + ); + memcpy(surface, image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Image) { + nall::image image = canvas.state.image; + image.scale(width, height); + image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0); + memcpy(surface, image.data, image.size); + } + + if(canvas.state.mode == Canvas::Mode::Data) { + if(width == canvas.state.width && height == canvas.state.height) { + memcpy(surface, canvas.state.data, width * height * sizeof(uint32_t)); + } else { + memset(surface, 0x00, width * height * sizeof(uint32_t)); + } + } + + surfaceWidth = width; + surfaceHeight = height; +} + +void pCanvas::redraw() { + InvalidateRect(hwnd, 0, false); +} + +void pCanvas::release() { + if(surface) { + delete[] surface; + surface = nullptr; + surfaceWidth = 0; + surfaceHeight = 0; + } +} + } diff --git a/phoenix/windows/widget/check-button.cpp b/phoenix/windows/widget/check-button.cpp index b8b40de0..f39e3cdf 100644 --- a/phoenix/windows/widget/check-button.cpp +++ b/phoenix/windows/widget/check-button.cpp @@ -1,37 +1,72 @@ namespace phoenix { -bool pCheckButton::checked() { - return SendMessage(hwnd, BM_GETCHECK, 0, 0); -} - Size pCheckButton::minimumSize() { Size size = pFont::size(hfont, checkButton.state.text); - return {size.width + 20, size.height + 4}; + + if(checkButton.state.orientation == Orientation::Horizontal) { + size.width += checkButton.state.image.width; + size.height = max(checkButton.state.image.height, size.height); + } + + if(checkButton.state.orientation == Orientation::Vertical) { + size.width = max(checkButton.state.image.width, size.width); + size.height += checkButton.state.image.height; + } + + return {size.width + 20, size.height + 10}; } void pCheckButton::setChecked(bool checked) { SendMessage(hwnd, BM_SETCHECK, (WPARAM)checked, 0); } +void pCheckButton::setImage(const image& image, Orientation orientation) { + nall::image nallImage = image; + nallImage.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0); + + if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } + if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; } + + if(OsVersion() < WindowsVista) nallImage.alphaBlend(GetSysColor(COLOR_BTNFACE)); + hbitmap = CreateBitmap(nallImage); + himagelist = ImageList_Create(nallImage.width, nallImage.height, ILC_COLOR32, 1, 0); + ImageList_Add(himagelist, hbitmap, NULL); + BUTTON_IMAGELIST list; + list.himl = himagelist; + switch(orientation) { + case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break; + case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break; + } + Button_SetImageList(hwnd, &list); + + setText(checkButton.state.text); +} + void pCheckButton::setText(string text) { + if(text.empty()) { + SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP); + } else { + SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~BS_BITMAP); + } + SetWindowText(hwnd, utf16_t(text)); } void pCheckButton::constructor() { - hwnd = CreateWindow( - L"BUTTON", L"", - WS_CHILD | WS_TABSTOP | BS_CHECKBOX, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 - ); + hwnd = CreateWindow(L"BUTTON", L"", + WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&checkButton); setDefaultFont(); - if(checkButton.state.checked) setChecked(true); - setText(checkButton.state.text); + setChecked(checkButton.state.checked); + setImage(checkButton.state.image, checkButton.state.orientation); +//setText(checkButton.state.text); synchronize(); - } void pCheckButton::destructor() { + if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } + if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; } DestroyWindow(hwnd); } @@ -40,4 +75,10 @@ void pCheckButton::orphan() { constructor(); } +void pCheckButton::onToggle() { + checkButton.state.checked = !checkButton.state.checked; + setChecked(checkButton.state.checked); + if(checkButton.onToggle) checkButton.onToggle(); +} + } diff --git a/phoenix/windows/widget/check-label.cpp b/phoenix/windows/widget/check-label.cpp new file mode 100644 index 00000000..35607b98 --- /dev/null +++ b/phoenix/windows/widget/check-label.cpp @@ -0,0 +1,44 @@ +namespace phoenix { + +Size pCheckLabel::minimumSize() { + Size size = pFont::size(hfont, checkLabel.state.text); + return {size.width + 20, size.height + 4}; +} + +void pCheckLabel::setChecked(bool checked) { + SendMessage(hwnd, BM_SETCHECK, (WPARAM)checked, 0); +} + +void pCheckLabel::setText(string text) { + SetWindowText(hwnd, utf16_t(text)); +} + +void pCheckLabel::constructor() { + hwnd = CreateWindow( + L"BUTTON", L"", + WS_CHILD | WS_TABSTOP | BS_CHECKBOX, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&checkLabel); + setDefaultFont(); + setChecked(checkLabel.state.checked); + setText(checkLabel.state.text); + synchronize(); +} + +void pCheckLabel::destructor() { + DestroyWindow(hwnd); +} + +void pCheckLabel::orphan() { + destructor(); + constructor(); +} + +void pCheckLabel::onToggle() { + checkLabel.state.checked = !checkLabel.state.checked; + setChecked(checkLabel.state.checked); + if(checkLabel.onToggle) checkLabel.onToggle(); +} + +} diff --git a/phoenix/windows/widget/combo-button.cpp b/phoenix/windows/widget/combo-button.cpp index 6f0d0235..26b3173f 100644 --- a/phoenix/windows/widget/combo-button.cpp +++ b/phoenix/windows/widget/combo-button.cpp @@ -11,33 +11,36 @@ Size pComboButton::minimumSize() { return {maximumWidth + 24, pFont::size(hfont, " ").height + 10}; } -void pComboButton::modify(unsigned row, string text) { +void pComboButton::remove(unsigned selection) { locked = true; - unsigned position = selection(); - SendMessage(hwnd, CB_DELETESTRING, row, 0); - SendMessage(hwnd, CB_INSERTSTRING, row, (LPARAM)(wchar_t*)utf16_t(text)); - setSelection(position); + SendMessage(hwnd, CB_DELETESTRING, selection, 0); locked = false; -} -void pComboButton::remove(unsigned row) { - locked = true; - unsigned position = selection(); - SendMessage(hwnd, CB_DELETESTRING, row, 0); - if(position == row) setSelection(0); - locked = false; + if(selection == comboButton.state.selection) comboButton.setSelection(0); } void pComboButton::reset() { SendMessage(hwnd, CB_RESETCONTENT, 0, 0); } -unsigned pComboButton::selection() { - return SendMessage(hwnd, CB_GETCURSEL, 0, 0); +void pComboButton::setGeometry(Geometry geometry) { + SetWindowPos(hwnd, NULL, geometry.x, geometry.y, geometry.width, 1, SWP_NOZORDER); + RECT rc; + GetWindowRect(hwnd, &rc); + unsigned adjustedHeight = geometry.height - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0)); + SendMessage(hwnd, CB_SETITEMHEIGHT, (WPARAM)-1, adjustedHeight); } -void pComboButton::setSelection(unsigned row) { - SendMessage(hwnd, CB_SETCURSEL, row, 0); +void pComboButton::setSelection(unsigned selection) { + SendMessage(hwnd, CB_SETCURSEL, selection, 0); +} + +void pComboButton::setText(unsigned selection, string text) { + locked = true; + SendMessage(hwnd, CB_DELETESTRING, selection, 0); + SendMessage(hwnd, CB_INSERTSTRING, selection, (LPARAM)(wchar_t*)utf16_t(text)); + setSelection(comboButton.state.selection); + locked = false; } void pComboButton::constructor() { @@ -45,7 +48,7 @@ void pComboButton::constructor() { L"COMBOBOX", L"", WS_CHILD | WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, 0, 0, 0, 0, - parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&comboButton); setDefaultFont(); @@ -63,12 +66,11 @@ void pComboButton::orphan() { constructor(); } -void pComboButton::setGeometry(Geometry geometry) { - SetWindowPos(hwnd, NULL, geometry.x, geometry.y, geometry.width, 1, SWP_NOZORDER); - RECT rc; - GetWindowRect(hwnd, &rc); - unsigned adjustedHeight = geometry.height - ((rc.bottom - rc.top) - SendMessage(hwnd, CB_GETITEMHEIGHT, (WPARAM)-1, 0)); - SendMessage(hwnd, CB_SETITEMHEIGHT, (WPARAM)-1, adjustedHeight); +void pComboButton::onChange() { + unsigned selection = SendMessage(hwnd, CB_GETCURSEL, 0, 0); + if(selection == comboButton.state.selection) return; + comboButton.state.selection = selection; + if(comboButton.onChange) comboButton.onChange(); } } diff --git a/phoenix/windows/widget/console.cpp b/phoenix/windows/widget/console.cpp index 86128588..74fe1b16 100644 --- a/phoenix/windows/widget/console.cpp +++ b/phoenix/windows/widget/console.cpp @@ -18,7 +18,7 @@ void pConsole::constructor() { hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, L"EDIT", L"", WS_CHILD | WS_TABSTOP | ES_READONLY | ES_MULTILINE | ES_WANTRETURN, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&console); setDefaultFont(); diff --git a/phoenix/windows/widget/frame.cpp b/phoenix/windows/widget/frame.cpp new file mode 100644 index 00000000..cb30c34a --- /dev/null +++ b/phoenix/windows/widget/frame.cpp @@ -0,0 +1,50 @@ +namespace phoenix { + +void pFrame::setEnabled(bool enabled) { + if(frame.state.layout) frame.state.layout->setEnabled(frame.state.layout->enabled()); + pWidget::setEnabled(enabled); +} + +void pFrame::setGeometry(Geometry geometry) { + bool empty = frame.state.text.empty(); + Size size = pFont::size(hfont, frame.state.text); + pWidget::setGeometry({ + geometry.x, geometry.y - (empty ? size.height >> 1 : 0), + geometry.width, geometry.height + (empty ? size.height >> 1 : 0) + }); + if(frame.state.layout == nullptr) return; + if(empty) size.height = 1; + geometry.x += 1, geometry.width -= 2; + geometry.y += size.height, geometry.height -= size.height + 2; + frame.state.layout->setGeometry(geometry); +} + +void pFrame::setText(string text) { + SetWindowText(hwnd, utf16_t(text)); +} + +void pFrame::setVisible(bool visible) { + if(frame.state.layout) frame.state.layout->setVisible(frame.state.layout->visible()); + pWidget::setVisible(visible); +} + +void pFrame::constructor() { + hwnd = CreateWindow(L"BUTTON", L"", + WS_CHILD | BS_GROUPBOX, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&frame); + setDefaultFont(); + setText(frame.state.text); + synchronize(); +} + +void pFrame::destructor() { + DestroyWindow(hwnd); +} + +void pFrame::orphan() { + destructor(); + constructor(); +} + +} diff --git a/phoenix/windows/widget/hex-edit.cpp b/phoenix/windows/widget/hex-edit.cpp index 13e85913..e39faf78 100644 --- a/phoenix/windows/widget/hex-edit.cpp +++ b/phoenix/windows/widget/hex-edit.cpp @@ -112,7 +112,7 @@ void pHexEdit::constructor() { hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, L"EDIT", L"", WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY | ES_MULTILINE | ES_WANTRETURN, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&hexEdit); diff --git a/phoenix/windows/widget/horizontal-scroller.cpp b/phoenix/windows/widget/horizontal-scroller.cpp index 8fe3c3d9..fe6e5a15 100644 --- a/phoenix/windows/widget/horizontal-scroller.cpp +++ b/phoenix/windows/widget/horizontal-scroller.cpp @@ -4,10 +4,6 @@ Size pHorizontalScroller::minimumSize() { return {0, 18}; } -unsigned pHorizontalScroller::position() { - return GetScrollPos(hwnd, SB_CTL); -} - void pHorizontalScroller::setLength(unsigned length) { length += (length == 0); SetScrollRange(hwnd, SB_CTL, 0, length - 1, TRUE); @@ -21,7 +17,7 @@ void pHorizontalScroller::setPosition(unsigned position) { void pHorizontalScroller::constructor() { hwnd = CreateWindow( L"SCROLLBAR", L"", WS_CHILD | WS_TABSTOP | SBS_HORZ, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&horizontalScroller); unsigned position = horizontalScroller.state.position; @@ -39,4 +35,11 @@ void pHorizontalScroller::orphan() { constructor(); } +void pHorizontalScroller::onChange(WPARAM wparam) { + unsigned position = ScrollEvent(hwnd, wparam); + if(position == horizontalScroller.state.position) return; + horizontalScroller.state.position = position; + if(horizontalScroller.onChange) horizontalScroller.onChange(); +} + } diff --git a/phoenix/windows/widget/horizontal-slider.cpp b/phoenix/windows/widget/horizontal-slider.cpp index b4e1bbd4..68547543 100644 --- a/phoenix/windows/widget/horizontal-slider.cpp +++ b/phoenix/windows/widget/horizontal-slider.cpp @@ -4,10 +4,6 @@ Size pHorizontalSlider::minimumSize() { return {0, 25}; } -unsigned pHorizontalSlider::position() { - return SendMessage(hwnd, TBM_GETPOS, 0, 0); -} - void pHorizontalSlider::setLength(unsigned length) { length += (length == 0); SendMessage(hwnd, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1)); @@ -22,7 +18,7 @@ void pHorizontalSlider::setPosition(unsigned position) { void pHorizontalSlider::constructor() { hwnd = CreateWindow( TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_HORZ, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&horizontalSlider); unsigned position = horizontalSlider.state.position; @@ -40,4 +36,11 @@ void pHorizontalSlider::orphan() { constructor(); } +void pHorizontalSlider::onChange() { + unsigned position = SendMessage(hwnd, TBM_GETPOS, 0, 0); + if(position == horizontalSlider.state.position) return; + horizontalSlider.state.position = position; + if(horizontalSlider.onChange) horizontalSlider.onChange(); +} + } diff --git a/phoenix/windows/widget/label.cpp b/phoenix/windows/widget/label.cpp index 26f6754b..335b9177 100644 --- a/phoenix/windows/widget/label.cpp +++ b/phoenix/windows/widget/label.cpp @@ -11,7 +11,9 @@ void pLabel::setText(string text) { } void pLabel::constructor() { - hwnd = CreateWindow(L"phoenix_label", L"", WS_CHILD, 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0); + hwnd = CreateWindow(L"phoenix_label", L"", + WS_CHILD, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&label); setDefaultFont(); setText(label.state.text); @@ -28,9 +30,11 @@ void pLabel::orphan() { } static LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - Window *window = (Window*)GetWindowLongPtr(GetParent(hwnd), GWLP_USERDATA); - Label *label = (Label*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if(!window || !label) return DefWindowProc(hwnd, msg, wparam, lparam); + HWND parentHwnd = GetParent(hwnd); + Label* label = (Label*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(label == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam); + Window* window = (Window*)label->Sizable::state.window; + if(window == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam); if(msg == WM_GETDLGCODE) { return DLGC_STATIC | DLGC_WANTCHARS; @@ -46,8 +50,8 @@ static LRESULT CALLBACK Label_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPA RECT rc; BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rc); - FillRect(ps.hdc, &rc, window->p.brush ? window->p.brush : GetSysColorBrush(COLOR_3DFACE)); - SetBkColor(ps.hdc, window->p.brush ? window->p.brushColor : GetSysColor(COLOR_3DFACE)); + DrawThemeParentBackground(hwnd, ps.hdc, &rc); + SetBkMode(ps.hdc, TRANSPARENT); SelectObject(ps.hdc, ((Widget*)label)->p.hfont); unsigned length = GetWindowTextLength(hwnd); wchar_t text[length + 1]; diff --git a/phoenix/windows/widget/line-edit.cpp b/phoenix/windows/widget/line-edit.cpp index 7f60b173..fd25b817 100644 --- a/phoenix/windows/widget/line-edit.cpp +++ b/phoenix/windows/widget/line-edit.cpp @@ -27,7 +27,7 @@ void pLineEdit::constructor() { hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, L"EDIT", L"", WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL | ES_AUTOVSCROLL, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&lineEdit); setDefaultFont(); @@ -46,4 +46,9 @@ void pLineEdit::orphan() { constructor(); } +void pLineEdit::onChange() { + if(locked) return; + if(lineEdit.onChange) lineEdit.onChange(); +} + } diff --git a/phoenix/windows/widget/list-view.cpp b/phoenix/windows/widget/list-view.cpp index 31dcaf32..79c8534c 100644 --- a/phoenix/windows/widget/list-view.cpp +++ b/phoenix/windows/widget/list-view.cpp @@ -27,7 +27,7 @@ void ImageList_Append(HIMAGELIST imageList, const nall::image& source) { auto image = source; if(image.empty()) { image.allocate(15, 15); - image.clear(GetSysColor(COLOR_WINDOW)); + image.fill(GetSysColor(COLOR_WINDOW)); } image.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0); image.scale(15, 15, Interpolation::Linear); @@ -60,19 +60,8 @@ void pListView::autoSizeColumns() { } } -bool pListView::checked(unsigned row) { - return ListView_GetCheckState(hwnd, row); -} - -void pListView::modify(unsigned row, const lstring& list) { - for(unsigned n = 0; n < list.size(); n++) { - utf16_t wtext(list(n, "")); - ListView_SetItemText(hwnd, row, n, wtext); - } -} - -void pListView::remove(unsigned row) { - ListView_DeleteItem(hwnd, row); +void pListView::remove(unsigned selection) { + ListView_DeleteItem(hwnd, selection); } void pListView::reset() { @@ -80,32 +69,21 @@ void pListView::reset() { buildImageList(); //free previously allocated images } -bool pListView::selected() { - unsigned count = ListView_GetItemCount(hwnd); - for(unsigned n = 0; n < count; n++) { - if(ListView_GetItemState(hwnd, n, LVIS_SELECTED)) return true; - } - return false; -} - -unsigned pListView::selection() { - unsigned count = ListView_GetItemCount(hwnd); - for(unsigned n = 0; n < count; n++) { - if(ListView_GetItemState(hwnd, n, LVIS_SELECTED)) return n; - } - return listView.state.selection; -} - void pListView::setCheckable(bool checkable) { ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | (checkable ? LVS_EX_CHECKBOXES : 0)); } -void pListView::setChecked(unsigned row, bool checked) { +void pListView::setChecked(unsigned selection, bool checked) { locked = true; - ListView_SetCheckState(hwnd, row, checked); + ListView_SetCheckState(hwnd, selection, checked); locked = false; } +void pListView::setGeometry(Geometry geometry) { + pWidget::setGeometry(geometry); + autoSizeColumns(); +} + void pListView::setHeaderText(const lstring& list) { while(ListView_DeleteColumn(hwnd, 0)); @@ -132,20 +110,20 @@ void pListView::setHeaderVisible(bool visible) { ); } -void pListView::setImage(unsigned row, unsigned column, const image& image) { +void pListView::setImage(unsigned selection, unsigned position, const image& image) { //assign existing image for(unsigned n = 0; n < images.size(); n++) { if(images[n] == image) { - imageMap(row)(column) = n; - return ListView_SetImage(hwnd, imageList, row, column, n); + imageMap(selection)(position) = n; + return ListView_SetImage(hwnd, imageList, selection, position, n); } } //append and assign new image - imageMap(row)(column) = images.size(); + imageMap(selection)(position) = images.size(); images.append(image); ImageList_Append(imageList, image); - ListView_SetImage(hwnd, imageList, row, column, imageMap(row)(column)); + ListView_SetImage(hwnd, imageList, selection, position, imageMap(selection)(position)); } void pListView::setSelected(bool selected) { @@ -159,19 +137,24 @@ void pListView::setSelected(bool selected) { locked = false; } -void pListView::setSelection(unsigned row) { +void pListView::setSelection(unsigned selection) { locked = true; lostFocus = false; - ListView_SetItemState(hwnd, row, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); + ListView_SetItemState(hwnd, selection, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED); locked = false; } +void pListView::setText(unsigned selection, unsigned position, string text) { + utf16_t wtext(text); + ListView_SetItemText(hwnd, selection, position, wtext); +} + void pListView::constructor() { lostFocus = false; hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", WS_CHILD | WS_TABSTOP | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | LVS_NOCOLUMNHEADER, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&listView); setDefaultFont(); @@ -195,11 +178,6 @@ void pListView::orphan() { constructor(); } -void pListView::setGeometry(Geometry geometry) { - pWidget::setGeometry(geometry); - autoSizeColumns(); -} - void pListView::buildImageList() { auto& list = listView.state.image; unsigned columns = listView.state.text.size(); @@ -244,4 +222,53 @@ void pListView::buildImageList() { } } +void pListView::onActivate(LPARAM lparam) { + LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam; + if(listView.state.text.empty() || !listView.state.selected) return; + if(listView.onActivate) listView.onActivate(); +} + +void pListView::onChange(LPARAM lparam) { + LPNMLISTVIEW nmlistview = (LPNMLISTVIEW)lparam; + if(!(nmlistview->uChanged & LVIF_STATE)) return; + + unsigned selection = nmlistview->iItem; + unsigned imagemask = ((nmlistview->uNewState & LVIS_STATEIMAGEMASK) >> 12) - 1; + if(imagemask == 0 || imagemask == 1) { + if(!locked) { + listView.state.checked[selection] = !listView.state.checked[selection]; + if(listView.onToggle) listView.onToggle(selection); + } + } else if((nmlistview->uOldState & LVIS_FOCUSED) && !(nmlistview->uNewState & LVIS_FOCUSED)) { + lostFocus = true; + } else if(!(nmlistview->uOldState & LVIS_SELECTED) && (nmlistview->uNewState & LVIS_SELECTED)) { + lostFocus = false; + listView.state.selected = true; + listView.state.selection = selection; + if(!locked && listView.onChange) listView.onChange(); + } else if(!lostFocus && !listView.state.selected) { + lostFocus = false; + listView.state.selected = false; + listView.state.selection = 0; + if(!locked && listView.onChange) listView.onChange(); + } +} + +LRESULT pListView::onCustomDraw(LPARAM lparam) { + LPNMLVCUSTOMDRAW lvcd = (LPNMLVCUSTOMDRAW)lparam; + + switch(lvcd->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW; + case CDDS_ITEMPREPAINT: + if(listView.state.headerText.size() >= 2) { + //draw alternating row colors of there are two or more columns + if(lvcd->nmcd.dwItemSpec % 2) lvcd->clrTextBk = GetSysColor(COLOR_WINDOW) ^ 0x070707; + } + return CDRF_DODEFAULT; + default: + return CDRF_DODEFAULT; + } +} + } diff --git a/phoenix/windows/widget/progress-bar.cpp b/phoenix/windows/widget/progress-bar.cpp index 4b438e8b..ebd24bc6 100644 --- a/phoenix/windows/widget/progress-bar.cpp +++ b/phoenix/windows/widget/progress-bar.cpp @@ -9,7 +9,9 @@ void pProgressBar::setPosition(unsigned position) { } void pProgressBar::constructor() { - hwnd = CreateWindow(PROGRESS_CLASS, L"", WS_CHILD | PBS_SMOOTH, 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0); + hwnd = CreateWindow(PROGRESS_CLASS, L"", + WS_CHILD | PBS_SMOOTH, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&progressBar); SendMessage(hwnd, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); SendMessage(hwnd, PBM_SETSTEP, MAKEWPARAM(1, 0), 0); diff --git a/phoenix/windows/widget/radio-button.cpp b/phoenix/windows/widget/radio-button.cpp index 7f1d5648..d7545cd4 100644 --- a/phoenix/windows/widget/radio-button.cpp +++ b/phoenix/windows/widget/radio-button.cpp @@ -1,12 +1,19 @@ namespace phoenix { -bool pRadioButton::checked() { - return SendMessage(hwnd, BM_GETCHECK, 0, 0); -} - Size pRadioButton::minimumSize() { Size size = pFont::size(hfont, radioButton.state.text); - return {size.width + 20, size.height + 4}; + + if(radioButton.state.orientation == Orientation::Horizontal) { + size.width += radioButton.state.image.width; + size.height = max(radioButton.state.image.height, size.height); + } + + if(radioButton.state.orientation == Orientation::Vertical) { + size.width = max(radioButton.state.image.width, size.width); + size.height += radioButton.state.image.height; + } + + return {size.width + 20, size.height + 10}; } void pRadioButton::setChecked() { @@ -18,24 +25,53 @@ void pRadioButton::setChecked() { void pRadioButton::setGroup(const group& group) { } +void pRadioButton::setImage(const image& image, Orientation orientation) { + nall::image nallImage = image; + nallImage.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0); + + if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } + if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; } + + if(OsVersion() < WindowsVista) nallImage.alphaBlend(GetSysColor(COLOR_BTNFACE)); + hbitmap = CreateBitmap(nallImage); + himagelist = ImageList_Create(nallImage.width, nallImage.height, ILC_COLOR32, 1, 0); + ImageList_Add(himagelist, hbitmap, NULL); + BUTTON_IMAGELIST list; + list.himl = himagelist; + switch(orientation) { + case Orientation::Horizontal: SetRect(&list.margin, 5, 0, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; break; + case Orientation::Vertical: SetRect(&list.margin, 0, 5, 0, 0); list.uAlign = BUTTON_IMAGELIST_ALIGN_TOP; break; + } + Button_SetImageList(hwnd, &list); + + setText(radioButton.state.text); +} + void pRadioButton::setText(string text) { + if(text.empty()) { + SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | BS_BITMAP); + } else { + SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~BS_BITMAP); + } + SetWindowText(hwnd, utf16_t(text)); } void pRadioButton::constructor() { - hwnd = CreateWindow( - L"BUTTON", L"", - WS_CHILD | WS_TABSTOP | BS_RADIOBUTTON, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 - ); + hwnd = CreateWindow(L"BUTTON", L"", + WS_CHILD | WS_TABSTOP | BS_CHECKBOX | BS_PUSHLIKE, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&radioButton); setDefaultFont(); if(radioButton.state.checked) setChecked(); - setText(radioButton.state.text); + setImage(radioButton.state.image, radioButton.state.orientation); +//setText(radioButton.state.text); synchronize(); } void pRadioButton::destructor() { + if(hbitmap) { DeleteObject(hbitmap); hbitmap = 0; } + if(himagelist) { ImageList_Destroy(himagelist); himagelist = 0; } DestroyWindow(hwnd); } @@ -44,4 +80,10 @@ void pRadioButton::orphan() { constructor(); } +void pRadioButton::onActivate() { + if(radioButton.state.checked) return; + radioButton.setChecked(); + if(radioButton.onActivate) radioButton.onActivate(); +} + } diff --git a/phoenix/windows/widget/radio-label.cpp b/phoenix/windows/widget/radio-label.cpp new file mode 100644 index 00000000..925f1cda --- /dev/null +++ b/phoenix/windows/widget/radio-label.cpp @@ -0,0 +1,49 @@ +namespace phoenix { + +Size pRadioLabel::minimumSize() { + Size size = pFont::size(hfont, radioLabel.state.text); + return {size.width + 20, size.height + 4}; +} + +void pRadioLabel::setChecked() { + for(auto& item : radioLabel.state.group) { + SendMessage(item.p.hwnd, BM_SETCHECK, (WPARAM)(&item == &radioLabel), 0); + } +} + +void pRadioLabel::setGroup(const group& group) { +} + +void pRadioLabel::setText(string text) { + SetWindowText(hwnd, utf16_t(text)); +} + +void pRadioLabel::constructor() { + hwnd = CreateWindow( + L"BUTTON", L"", + WS_CHILD | WS_TABSTOP | BS_RADIOBUTTON, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 + ); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&radioLabel); + setDefaultFont(); + if(radioLabel.state.checked) setChecked(); + setText(radioLabel.state.text); + synchronize(); +} + +void pRadioLabel::destructor() { + DestroyWindow(hwnd); +} + +void pRadioLabel::orphan() { + destructor(); + constructor(); +} + +void pRadioLabel::onActivate() { + if(radioLabel.state.checked) return; + radioLabel.setChecked(); + if(radioLabel.onActivate) radioLabel.onActivate(); +} + +} diff --git a/phoenix/windows/widget/tab-frame.cpp b/phoenix/windows/widget/tab-frame.cpp new file mode 100644 index 00000000..9a3abb64 --- /dev/null +++ b/phoenix/windows/widget/tab-frame.cpp @@ -0,0 +1,143 @@ +namespace phoenix { + +static LRESULT CALLBACK TabFrame_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + Object* object = (Object*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if(object == nullptr) return DefWindowProc(hwnd, msg, wparam, lparam); + TabFrame& tabFrame = (TabFrame&)*object; + return Shared_windowProc(tabFrame.p.windowProc, hwnd, msg, wparam, lparam); +} + +void pTabFrame::append(string text, const image& image) { + unsigned selection = TabCtrl_GetItemCount(hwnd); + wchar_t wtext[] = L""; + TCITEM item; + item.mask = TCIF_TEXT; + item.pszText = wtext; + TabCtrl_InsertItem(hwnd, selection, &item); + setText(selection, text); + if(!image.empty()) setImage(selection, image); +} + +void pTabFrame::remove(unsigned selection) { + TabCtrl_DeleteItem(hwnd, selection); + buildImageList(); +} + +void pTabFrame::setEnabled(bool enabled) { + pWidget::setEnabled(enabled); + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setEnabled(layout->enabled()); + } +} + +void pTabFrame::setGeometry(Geometry geometry) { + pWidget::setGeometry(geometry); + geometry.x += 1, geometry.width -= 4; + geometry.y += 21, geometry.height -= 23; + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setGeometry(geometry); + } +} + +void pTabFrame::setImage(unsigned selection, const image& image) { + buildImageList(); +} + +void pTabFrame::setSelection(unsigned selection) { + TabCtrl_SetCurSel(hwnd, selection); + synchronizeLayout(); +} + +void pTabFrame::setText(unsigned selection, string text) { + utf16_t wtext(text); + TCITEM item; + item.mask = TCIF_TEXT; + item.pszText = (wchar_t*)wtext; + TabCtrl_SetItem(hwnd, selection, &item); +} + +void pTabFrame::setVisible(bool visible) { + pWidget::setVisible(visible); + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setVisible(layout->visible()); + } +} + +void pTabFrame::constructor() { + hwnd = CreateWindow(WC_TABCONTROL, L"", + WS_CHILD | WS_TABSTOP, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&tabFrame); + + windowProc = (WindowProc)GetWindowLongPtr(hwnd, GWLP_WNDPROC); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)TabFrame_windowProc); + + setDefaultFont(); + for(auto& text : tabFrame.state.text) append(text, {}); + buildImageList(); + setSelection(tabFrame.state.selection); + synchronize(); +} + +void pTabFrame::destructor() { + if(imageList) { ImageList_Destroy(imageList); imageList = nullptr; } + DestroyWindow(hwnd); +} + +void pTabFrame::orphan() { + destructor(); + constructor(); +} + +void pTabFrame::buildImageList() { + if(imageList) ImageList_Destroy(imageList); + unsigned size = pFont::size(hfont, " ").height; + imageList = ImageList_Create(size, size, ILC_COLOR32, 1, 0); + for(auto& image : tabFrame.state.image) { + ImageList_Append(imageList, image); + } + TabCtrl_SetImageList(hwnd, imageList); + for(unsigned n = 0; n < tabFrame.state.image.size(); n++) { + TCITEM item; + item.mask = TCIF_IMAGE; + item.iImage = (tabFrame.state.image(n).empty() ? -1 : n); + TabCtrl_SetItem(hwnd, n, &item); + } +} + +void pTabFrame::synchronizeLayout() { + unsigned selection = 0; + for(auto& layout : tabFrame.state.layout) { + if(layout) layout->setVisible(selection == tabFrame.state.selection); + selection++; + } +} + +void pTabFrame::onChange() { + tabFrame.state.selection = TabCtrl_GetCurSel(hwnd); + synchronizeLayout(); + if(tabFrame.onChange) tabFrame.onChange(); +} + +//called only if TCS_OWNERDRAWFIXED style is used +//this style disables XP/Vista theming of the TabFrame +void pTabFrame::onDrawItem(LPARAM lparam) { + LPDRAWITEMSTRUCT item = (LPDRAWITEMSTRUCT)lparam; + FillRect(item->hDC, &item->rcItem, GetSysColorBrush(COLOR_3DFACE)); + SetBkMode(item->hDC, TRANSPARENT); + SetTextColor(item->hDC, GetSysColor(COLOR_BTNTEXT)); + + unsigned selection = item->itemID; + if(selection < tabFrame.state.text.size()) { + string text = tabFrame.state.text[selection]; + Size size = pFont::size(hfont, text); + unsigned width = item->rcItem.right - item->rcItem.left + 1; + if(!tabFrame.state.image[selection].empty()) { + width += size.height + 2; + ImageList_Draw(imageList, selection, item->hDC, item->rcItem.left + (width - size.width) / 2 - (size.height + 3), item->rcItem.top + 2, ILD_NORMAL); + } + TextOut(item->hDC, item->rcItem.left + (width - size.width) / 2, item->rcItem.top + 2, utf16_t(text), text.size()); + } +} + +} diff --git a/phoenix/windows/widget/text-edit.cpp b/phoenix/windows/widget/text-edit.cpp index 2433320a..ae3e8ce9 100644 --- a/phoenix/windows/widget/text-edit.cpp +++ b/phoenix/windows/widget/text-edit.cpp @@ -39,7 +39,7 @@ void pTextEdit::constructor() { hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, L"EDIT", L"", WS_CHILD | WS_TABSTOP | WS_VSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN | (textEdit.state.wordWrap == false ? WS_HSCROLL | ES_AUTOHSCROLL : 0), - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&textEdit); setDefaultFont(); @@ -59,4 +59,9 @@ void pTextEdit::orphan() { constructor(); } +void pTextEdit::onChange() { + if(locked) return; + if(textEdit.onChange) textEdit.onChange(); +} + } diff --git a/phoenix/windows/widget/vertical-scroller.cpp b/phoenix/windows/widget/vertical-scroller.cpp index 84a3b74c..009779ca 100644 --- a/phoenix/windows/widget/vertical-scroller.cpp +++ b/phoenix/windows/widget/vertical-scroller.cpp @@ -4,10 +4,6 @@ Size pVerticalScroller::minimumSize() { return {18, 0}; } -unsigned pVerticalScroller::position() { - return GetScrollPos(hwnd, SB_CTL); -} - void pVerticalScroller::setLength(unsigned length) { length += (length == 0); SetScrollRange(hwnd, SB_CTL, 0, length - 1, TRUE); @@ -21,7 +17,7 @@ void pVerticalScroller::setPosition(unsigned position) { void pVerticalScroller::constructor() { hwnd = CreateWindow( L"SCROLLBAR", L"", WS_CHILD | SBS_VERT, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&verticalScroller); unsigned position = verticalScroller.state.position; @@ -39,4 +35,11 @@ void pVerticalScroller::orphan() { constructor(); } +void pVerticalScroller::onChange(WPARAM wparam) { + unsigned position = ScrollEvent(hwnd, wparam); + if(position == verticalScroller.state.position) return; + verticalScroller.state.position = position; + if(verticalScroller.onChange) verticalScroller.onChange(); +} + } diff --git a/phoenix/windows/widget/vertical-slider.cpp b/phoenix/windows/widget/vertical-slider.cpp index c4924557..03bde0ed 100644 --- a/phoenix/windows/widget/vertical-slider.cpp +++ b/phoenix/windows/widget/vertical-slider.cpp @@ -4,10 +4,6 @@ Size pVerticalSlider::minimumSize() { return {0, 25}; } -unsigned pVerticalSlider::position() { - return SendMessage(hwnd, TBM_GETPOS, 0, 0); -} - void pVerticalSlider::setLength(unsigned length) { length += (length == 0); SendMessage(hwnd, TBM_SETRANGE, (WPARAM)true, (LPARAM)MAKELONG(0, length - 1)); @@ -22,7 +18,7 @@ void pVerticalSlider::setPosition(unsigned position) { void pVerticalSlider::constructor() { hwnd = CreateWindow( TRACKBAR_CLASS, L"", WS_CHILD | WS_TABSTOP | TBS_NOTICKS | TBS_BOTH | TBS_VERT, - 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&verticalSlider); unsigned position = verticalSlider.state.position; @@ -40,4 +36,11 @@ void pVerticalSlider::orphan() { constructor(); } +void pVerticalSlider::onChange() { + unsigned position = SendMessage(hwnd, TBM_GETPOS, 0, 0); + if(position == verticalSlider.state.position) return; + verticalSlider.state.position = position; + if(verticalSlider.onChange) verticalSlider.onChange(); +} + } diff --git a/phoenix/windows/widget/viewport.cpp b/phoenix/windows/widget/viewport.cpp index 3bda4529..e3400965 100644 --- a/phoenix/windows/widget/viewport.cpp +++ b/phoenix/windows/widget/viewport.cpp @@ -56,7 +56,9 @@ void pViewport::setDroppable(bool droppable) { } void pViewport::constructor() { - hwnd = CreateWindow(L"phoenix_viewport", L"", WS_CHILD | WS_DISABLED, 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0); + hwnd = CreateWindow(L"phoenix_viewport", L"", + WS_CHILD | WS_DISABLED, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&viewport); setDroppable(viewport.state.droppable); synchronize(); diff --git a/phoenix/windows/widget/widget.cpp b/phoenix/windows/widget/widget.cpp index fbec9a32..2f09b00d 100644 --- a/phoenix/windows/widget/widget.cpp +++ b/phoenix/windows/widget/widget.cpp @@ -1,9 +1,5 @@ namespace phoenix { -bool pWidget::enabled() { - return IsWindowEnabled(hwnd); -} - bool pWidget::focused() { return GetFocus() == hwnd; } @@ -13,8 +9,9 @@ Size pWidget::minimumSize() { } void pWidget::setEnabled(bool enabled) { + if(!widget.parent()) enabled = false; if(widget.state.abstract) enabled = false; - if(sizable.state.layout && sizable.state.layout->enabled() == false) enabled = false; + if(!widget.enabledToAll()) enabled = false; EnableWindow(hwnd, enabled); } @@ -29,19 +26,28 @@ void pWidget::setFont(string font) { } void pWidget::setGeometry(Geometry geometry) { + if(GetParentWidget(&sizable)) { + Position displacement = GetParentWidget(&sizable)->state.geometry.position(); + geometry.x -= displacement.x; + geometry.y -= displacement.y; + } SetWindowPos(hwnd, NULL, geometry.x, geometry.y, geometry.width, geometry.height, SWP_NOZORDER); + if(widget.onSize) widget.onSize(); } void pWidget::setVisible(bool visible) { + if(!widget.parent()) visible = false; if(widget.state.abstract) visible = false; - if(sizable.state.layout && sizable.state.layout->visible() == false) visible = false; + if(!widget.visibleToAll()) visible = false; ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE); } void pWidget::constructor() { - hfont = pFont::create("Tahoma, 8"); + hfont = pFont::create(Font::sans(8)); if(widget.state.abstract) { - hwnd = CreateWindow(L"phoenix_label", L"", WS_CHILD, 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0); + hwnd = CreateWindow(L"phoenix_label", L"", + WS_CHILD, + 0, 0, 0, 0, parentHwnd, (HMENU)id, GetModuleHandle(0), 0); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&widget); } } @@ -59,16 +65,18 @@ void pWidget::orphan() { void pWidget::setDefaultFont() { string description = widget.state.font; - if(description.empty()) description = "Tahoma, 8"; + if(description.empty()) description = Font::sans(8); hfont = pFont::create(description); SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, 0); } //calling Widget::setParent destroys widget and re-creates it: //need to re-apply visiblity and enabled status; called by each subclassed setParent() function +//constructors are called top-down, so set each widget to the top of the z-order (so children appear on top of parents) void pWidget::synchronize() { widget.setEnabled(widget.enabled()); widget.setVisible(widget.visible()); + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } } diff --git a/phoenix/windows/window.cpp b/phoenix/windows/window.cpp index 16223da4..400f040b 100644 --- a/phoenix/windows/window.cpp +++ b/phoenix/windows/window.cpp @@ -58,7 +58,11 @@ void pWindow::append(Menu& menu) { } void pWindow::append(Widget& widget) { - widget.p.parentWindow = &window; + if(GetParentWidget(&widget)) { + widget.p.parentHwnd = GetParentWidget(&widget)->p.hwnd; + } else { + widget.p.parentHwnd = window.p.hwnd; + } widget.p.orphan(); if(widget.font().empty() && !window.state.widgetFont.empty()) { @@ -66,12 +70,6 @@ void pWindow::append(Widget& widget) { } } -Color pWindow::backgroundColor() { - if(window.state.backgroundColorOverride) return window.state.backgroundColor; - DWORD color = GetSysColor(COLOR_3DFACE); - return {(uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color >> 0), 255u}; -} - bool pWindow::focused() { return (GetForegroundWindow() == hwnd); } @@ -232,7 +230,7 @@ void pWindow::constructor() { hmenu = CreateMenu(); hstatus = CreateWindow(STATUSCLASSNAME, L"", WS_CHILD, 0, 0, 0, 0, hwnd, 0, GetModuleHandle(0), 0); hstatusfont = 0; - setStatusFont("Tahoma, 8"); + setStatusFont(Font::sans(8)); //status bar will be capable of receiving tab focus if it is not disabled SetWindowLongPtr(hstatus, GWL_STYLE, GetWindowLong(hstatus, GWL_STYLE) | WS_DISABLED); @@ -240,6 +238,9 @@ void pWindow::constructor() { SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&window); setDroppable(window.state.droppable); setGeometry({128, 128, 256, 256}); + + DWORD color = GetSysColor(COLOR_3DFACE); + window.state.backgroundColor = Color((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color >> 0), 255u); } void pWindow::destructor() { @@ -263,4 +264,62 @@ void pWindow::updateMenu() { SetMenu(hwnd, window.state.menuVisible ? hmenu : 0); } +void pWindow::onClose() { + if(window.onClose) window.onClose(); + else window.setVisible(false); + if(window.state.modal && !window.state.visible) window.setModal(false); +} + +void pWindow::onDrop(WPARAM wparam) { + lstring paths = DropPaths(wparam); + if(paths.empty()) return; + if(window.onDrop) window.onDrop(paths); +} + +bool pWindow::onEraseBackground() { + if(brush == 0) return false; + RECT rc; + GetClientRect(hwnd, &rc); + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + FillRect(ps.hdc, &rc, brush); + EndPaint(hwnd, &ps); + return true; +} + +void pWindow::onModalBegin() { + if(Application::Windows::onModalBegin) Application::Windows::onModalBegin(); +} + +void pWindow::onModalEnd() { + if(Application::Windows::onModalEnd) Application::Windows::onModalEnd(); +} + +void pWindow::onMove() { + if(locked) return; + + Geometry windowGeometry = geometry(); + window.state.geometry.x = windowGeometry.x; + window.state.geometry.y = windowGeometry.y; + + if(window.onMove) window.onMove(); +} + +void pWindow::onSize() { + if(locked) return; + SetWindowPos(hstatus, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED); + + Geometry windowGeometry = geometry(); + window.state.geometry.width = windowGeometry.width; + window.state.geometry.height = windowGeometry.height; + + for(auto& layout : window.state.layout) { + Geometry geom = geometry(); + geom.x = geom.y = 0; + layout.setGeometry(geom); + } + + if(window.onSize) window.onSize(); +} + } diff --git a/ruby/audio/openal.cpp b/ruby/audio/openal.cpp index 95c66138..af478be0 100644 --- a/ruby/audio/openal.cpp +++ b/ruby/audio/openal.cpp @@ -4,7 +4,7 @@ contributors: byuu, wertigon, _willow_ */ -#if defined(PLATFORM_OSX) +#if defined(PLATFORM_MACOSX) #include #include #else diff --git a/ruby/implementation.cpp b/ruby/implementation.cpp index 7b13504d..723f86bd 100644 --- a/ruby/implementation.cpp +++ b/ruby/implementation.cpp @@ -4,7 +4,7 @@ #include #include #include -#elif defined(PLATFORM_OSX) +#elif defined(PLATFORM_MACOSX) #define decimal CocoaDecimal #include #include diff --git a/ruby/video/opengl/bind.hpp b/ruby/video/opengl/bind.hpp index e74780e0..196a627f 100644 --- a/ruby/video/opengl/bind.hpp +++ b/ruby/video/opengl/bind.hpp @@ -1,5 +1,8 @@ -#if !defined(PLATFORM_OSX) - +#if defined(PLATFORM_MACOSX) +static bool OpenGLBind() { + return true; +} +#else PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr; PFNGLDELETEPROGRAMPROC glDeleteProgram = nullptr; PFNGLUSEPROGRAMPROC glUseProgram = nullptr; @@ -93,11 +96,4 @@ static bool OpenGLBind() { return true; } - -#else - -static bool OpenGLBind() { - return true; -} - #endif diff --git a/target-ethos/ethos.cpp b/target-ethos/ethos.cpp index 5f282bff..4b74f522 100644 --- a/target-ethos/ethos.cpp +++ b/target-ethos/ethos.cpp @@ -53,17 +53,10 @@ Program::Program(int argc, char** argv) { bootstrap(); active = nullptr; - if(Intrinsics::platform() == Intrinsics::Platform::MacOSX) { - normalFont = Font::sans(12); - boldFont = Font::sans(12, "Bold"); - titleFont = Font::sans(20, "Bold"); - monospaceFont = Font::monospace(8); - } else { - normalFont = Font::sans(8); - boldFont = Font::sans(8, "Bold"); - titleFont = Font::sans(16, "Bold"); - monospaceFont = Font::monospace(8); - } + normalFont = Font::sans(8); + boldFont = Font::sans(8, "Bold"); + titleFont = Font::sans(16, "Bold"); + monospaceFont = Font::monospace(8); config = new ConfigurationSettings; video.driver(config->video.driver); @@ -87,6 +80,7 @@ Program::Program(int argc, char** argv) { cheatDatabase = new CheatDatabase; cheatEditor = new CheatEditor; stateManager = new StateManager; + tools = new Tools; windowManager->loadGeometry(); presentation->setVisible(); utility->resize(); @@ -157,7 +151,6 @@ int main(int argc, char** argv) { Application::Cocoa::onPreferences = [&] { settings->setVisible(); - settings->panelList.setFocused(); }; Application::Cocoa::onQuit = [&] { diff --git a/target-ethos/general/presentation.cpp b/target-ethos/general/presentation.cpp index 0605d587..e26f4e8c 100644 --- a/target-ethos/general/presentation.cpp +++ b/target-ethos/general/presentation.cpp @@ -91,20 +91,31 @@ Presentation::Presentation() { append(loadMenu); for(auto& item : loadListSystem) loadMenu.append(*item); - if(program->ananke.open()) loadMenu.append(loadSeparator, loadImport); + if(program->ananke.open()) { + loadMenu.append(loadSeparator); + loadMenu.append(loadImport); + } for(auto& systemItem : emulatorList) append(systemItem->menu); append(settingsMenu); settingsMenu.append(videoMenu); - videoMenu.append(centerVideo, scaleVideo, stretchVideo, *new Separator, aspectCorrection, maskOverscan); + videoMenu.append(centerVideo); + videoMenu.append(scaleVideo); + videoMenu.append(stretchVideo); + videoMenu.append(*new Separator); + videoMenu.append(aspectCorrection); + videoMenu.append(maskOverscan); settingsMenu.append(shaderMenu); - shaderMenu.append(shaderNone, shaderBlur); + shaderMenu.append(shaderNone); + shaderMenu.append(shaderBlur); if(config->video.driver == "OpenGL") shaderMenu.append(shaderEmulation); if(shaderList.size() > 0) { shaderMenu.append(*new Separator); for(auto& shader : shaderList) shaderMenu.append(*shader); } settingsMenu.append(*new Separator); - settingsMenu.append(synchronizeVideo, synchronizeAudio, muteAudio); + settingsMenu.append(synchronizeVideo); + settingsMenu.append(synchronizeAudio); + settingsMenu.append(muteAudio); if(Intrinsics::platform() != Intrinsics::Platform::MacOSX) { settingsMenu.append(*new Separator); settingsMenu.append(configurationSettings); @@ -115,7 +126,10 @@ Presentation::Presentation() { toolsMenu.append(loadStateMenu); for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]); toolsMenu.append(stateMenuSeparator); - toolsMenu.append(resizeWindow, stateManager, cheatEditor, synchronizeTime); + toolsMenu.append(resizeWindow); + toolsMenu.append(stateManager); + toolsMenu.append(cheatEditor); + toolsMenu.append(synchronizeTime); append(layout); layout.append(viewport, {0, 0, 1, 1}); @@ -160,12 +174,12 @@ Presentation::Presentation() { synchronizeVideo.onToggle = [&] { config->video.synchronize = synchronizeVideo.checked(); utility->synchronizeRuby(); }; synchronizeAudio.onToggle = [&] { config->audio.synchronize = synchronizeAudio.checked(); utility->synchronizeRuby(); }; muteAudio.onToggle = [&] { config->audio.mute = muteAudio.checked(); utility->synchronizeRuby(); }; - configurationSettings.onActivate = [&] { settings->setVisible(); settings->panelList.setFocused(); }; + configurationSettings.onActivate = [&] { settings->setVisible(); }; for(unsigned n = 0; n < 5; n++) saveStateItem[n].onActivate = [=] { utility->saveState(1 + n); }; for(unsigned n = 0; n < 5; n++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); }; resizeWindow.onActivate = [&] { utility->resize(true); }; - stateManager.onActivate = [&] { ::stateManager->setVisible(); }; - cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); }; + stateManager.onActivate = [&] { tools->panels.setSelection(1); tools->setVisible(); }; + cheatEditor.onActivate = [&] { tools->panels.setSelection(0); tools->setVisible(); }; synchronizeTime.onActivate = [&] { system().rtcsync(); }; synchronize(); diff --git a/target-ethos/resource/advanced.png b/target-ethos/resource/advanced.png new file mode 100644 index 00000000..9460dfc7 Binary files /dev/null and b/target-ethos/resource/advanced.png differ diff --git a/target-ethos/resource/audio.png b/target-ethos/resource/audio.png new file mode 100644 index 00000000..36ca7b08 Binary files /dev/null and b/target-ethos/resource/audio.png differ diff --git a/target-ethos/resource/cheat-editor.png b/target-ethos/resource/cheat-editor.png new file mode 100644 index 00000000..c8d899cf Binary files /dev/null and b/target-ethos/resource/cheat-editor.png differ diff --git a/target-ethos/resource/hotkeys.png b/target-ethos/resource/hotkeys.png new file mode 100644 index 00000000..fab414b8 Binary files /dev/null and b/target-ethos/resource/hotkeys.png differ diff --git a/target-ethos/resource/input.png b/target-ethos/resource/input.png new file mode 100644 index 00000000..9d040ee8 Binary files /dev/null and b/target-ethos/resource/input.png differ diff --git a/target-ethos/resource/resource.bml b/target-ethos/resource/resource.bml index 763b03a6..e9a78611 100644 --- a/target-ethos/resource/resource.bml +++ b/target-ethos/resource/resource.bml @@ -1,6 +1,15 @@ resource name=resource - binary id=home name=home.png - binary id=up name=up.png + binary id=advanced name=advanced.png + binary id=audio name=audio.png + binary id=cheatEditor name=cheat-editor.png binary id=folder name=folder.png binary id=game name=game.png + binary id=home name=home.png + binary id=hotkeys name=hotkeys.png + binary id=input name=input.png + binary id=server name=server.png + binary id=stateManager name=state-manager.png + binary id=timing name=timing.png binary id=unverified name=unverified.png + binary id=up name=up.png + binary id=video name=video.png diff --git a/target-ethos/resource/resource.cpp b/target-ethos/resource/resource.cpp index f0f42c04..1f84fc61 100644 --- a/target-ethos/resource/resource.cpp +++ b/target-ethos/resource/resource.cpp @@ -1,49 +1,81 @@ namespace resource { -const uint8_t home[606] = { +const uint8_t advanced[611] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, - 97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0, - 0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,10,14,20,37,19,83,42,210,59,0,0,1,235,73, - 68,65,84,56,203,149,147,191,107,83,81,20,128,191,123,251,222,75,211,64,242,36,160,85,135,100,81,123,19,104,85,172, - 17,92,28,140,66,19,167,135,212,74,39,145,162,163,24,92,58,180,110,193,169,254,24,234,212,37,139,245,199,96,19,240, - 15,240,63,16,121,91,92,196,90,219,240,98,81,137,33,121,215,33,246,217,151,80,33,103,186,92,206,247,221,115,14,231, - 194,1,225,41,85,172,128,174,128,246,148,122,200,48,177,7,123,74,105,79,169,61,73,113,104,120,177,56,163,203,55,111, - 252,87,34,250,225,154,235,110,20,148,98,253,220,89,182,39,78,113,228,240,56,39,188,38,83,107,107,212,92,151,130,82, - 215,14,185,110,117,64,176,31,126,83,156,97,247,248,49,148,202,50,22,141,210,106,181,24,219,252,74,182,92,30,144,136, - 126,184,122,251,22,157,100,146,116,42,77,42,149,2,64,107,104,183,219,236,214,235,156,44,149,66,18,177,31,126,247,160, - 68,199,48,201,229,46,96,219,118,208,154,214,26,0,223,247,249,185,181,69,114,110,46,144,136,10,232,130,82,188,127,84, - 198,107,54,57,63,157,35,30,143,35,132,8,9,124,223,15,206,134,16,140,230,243,212,92,23,3,184,3,172,78,157,62, - 195,253,210,61,54,170,111,3,240,201,202,51,86,30,63,69,139,17,116,167,133,48,70,209,221,223,44,47,45,209,238,165, - 204,202,121,120,190,48,153,33,17,79,0,160,212,4,153,140,34,155,205,244,94,20,146,145,244,149,222,196,83,121,144,22, - 90,107,22,38,51,204,195,186,236,239,211,178,44,76,211,196,48,140,224,254,75,227,7,0,31,234,59,116,187,126,144,11, - 48,32,144,82,34,165,196,146,159,1,232,96,241,241,83,3,128,111,222,47,58,34,18,90,36,99,96,179,254,14,207,231, - 40,0,151,46,78,115,53,26,67,234,113,46,75,147,237,77,66,21,24,253,21,252,155,126,239,165,239,222,14,162,217,56, - 112,245,3,65,36,18,193,182,109,18,137,4,2,137,16,16,139,197,112,28,103,0,178,44,43,252,23,174,207,58,175,0, - 135,225,98,245,229,139,215,119,255,0,86,248,213,163,133,187,128,26,0,0,0,0,73,69,78,68,174,66,96,130, + 97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0, + 0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,9,26,12,19,57,139,99,194,72,0,0,1,240,73, + 68,65,84,56,203,165,146,75,104,19,97,20,133,191,153,248,76,138,77,20,10,130,74,176,204,198,133,182,221,69,172,46, + 52,20,98,102,235,66,112,33,84,164,168,181,45,18,82,2,21,3,67,66,160,107,65,116,101,64,138,226,194,137,141,208, + 98,87,130,210,54,137,73,23,21,130,85,105,211,151,134,32,68,68,58,51,110,102,134,49,29,235,194,179,251,185,231,156, + 123,239,185,63,252,39,4,231,35,157,81,162,128,10,200,241,88,34,247,23,77,52,157,81,108,142,216,82,84,251,175,246, + 3,168,166,153,171,248,250,181,1,205,108,180,29,233,140,98,52,26,117,35,157,81,12,247,90,195,170,69,1,196,116,70, + 137,90,100,171,171,215,219,230,102,28,5,120,240,240,62,241,88,66,6,114,0,34,160,14,222,28,50,158,62,155,48,0, + 245,246,173,17,54,215,215,144,58,37,234,245,175,247,90,179,137,221,25,21,44,49,128,96,21,111,12,12,26,154,182,37, + 232,186,110,232,186,70,97,126,78,40,188,47,2,200,59,5,43,180,116,176,17,233,187,72,185,82,97,185,246,5,32,25, + 143,37,238,254,243,140,78,188,202,191,52,74,149,18,221,39,123,40,150,11,152,19,76,2,186,171,65,48,146,58,12,212, + 172,247,149,144,78,215,9,137,163,71,142,209,108,54,121,55,247,22,183,53,60,78,241,248,112,152,190,80,39,29,1,47, + 217,153,77,142,183,127,75,46,125,94,58,215,125,170,135,128,63,192,74,109,249,242,133,240,249,249,233,169,215,85,192,0, + 16,131,145,84,23,80,123,52,38,51,91,94,100,182,188,72,199,65,31,0,230,222,114,46,255,2,73,146,56,123,186,215, + 250,100,118,30,34,80,28,31,14,51,245,166,196,150,166,35,238,243,179,81,111,218,35,154,35,203,217,39,143,57,208,238, + 231,76,168,23,96,204,105,0,192,238,93,30,246,248,14,241,243,199,47,178,249,5,128,112,171,73,46,175,82,253,88,253, + 35,108,17,96,101,227,59,251,125,1,218,246,122,120,62,243,1,32,252,105,114,116,218,73,52,77,146,107,235,171,11,64, + 210,190,66,48,146,186,4,76,56,184,219,196,59,225,55,55,226,213,246,234,188,84,188,0,0,0,0,73,69,78,68,174, + 66,96,130, }; -const uint8_t up[652] = { +const uint8_t audio[592] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, 97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114, - 101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,30,73,68,65,84,56,141, - 149,147,79,104,19,65,20,198,191,55,187,51,217,141,133,122,104,76,255,209,130,4,237,193,64,42,168,208,213,138,104,68, - 98,69,45,20,114,107,74,201,73,144,98,22,193,171,199,82,145,160,23,15,69,79,30,165,66,241,226,77,42,94,68,80, - 208,67,41,180,42,149,148,52,137,166,154,38,217,217,25,15,81,172,33,169,246,29,223,251,230,199,124,223,155,33,173,53, - 218,213,201,155,124,30,0,150,102,189,233,118,26,214,110,224,184,60,221,223,21,73,246,117,29,76,58,46,79,239,9,224, - 184,60,26,16,251,178,87,206,76,6,199,70,147,193,128,8,102,29,151,71,255,11,224,184,188,3,132,197,137,115,211,214, - 150,151,199,150,183,137,248,200,37,11,132,69,199,229,29,255,4,16,225,209,232,112,162,167,55,52,72,159,75,31,240,177, - 244,30,157,157,157,20,27,58,209,13,194,195,93,1,142,203,211,253,7,34,137,179,71,199,249,74,254,13,12,198,97,50, - 142,87,107,11,136,13,29,23,161,253,61,137,230,60,216,142,195,81,193,237,108,42,225,218,235,229,101,104,242,32,132,137, - 58,125,199,54,21,241,46,255,28,23,78,95,13,154,166,248,43,15,182,211,247,212,88,198,82,228,161,34,75,16,1,1, - 30,224,40,203,117,8,219,64,65,174,162,168,86,113,254,212,69,139,216,159,60,216,111,223,241,99,227,221,135,250,98,148, - 175,172,193,228,6,56,55,80,81,5,84,141,175,16,54,131,176,25,150,191,189,64,184,55,68,71,14,15,135,137,53,242, - 160,145,140,153,30,232,142,100,111,76,220,177,5,15,64,65,194,135,135,215,185,5,188,45,60,131,100,21,16,35,36,6, - 50,208,90,67,41,160,94,175,225,241,211,249,74,177,180,57,99,18,67,234,83,110,197,158,185,119,185,225,137,163,122,247, - 250,19,171,44,115,240,141,109,112,193,192,76,130,193,9,115,15,110,87,101,77,91,191,236,7,137,33,101,46,205,122,78, - 211,38,180,214,10,165,250,23,48,131,96,112,6,30,104,64,100,77,91,47,231,60,218,245,29,0,128,210,62,126,212,139, - 141,43,251,26,178,174,160,85,235,63,99,182,106,250,74,162,90,219,134,50,53,160,21,124,73,240,189,61,0,164,47,17, - 31,188,6,98,0,49,2,17,246,0,32,108,220,186,63,25,110,37,38,194,70,115,239,39,48,247,197,219,182,208,154,34, - 0,0,0,0,73,69,78,68,174,66,96,130, + 101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,1,226,73,68,65,84,56,141, + 165,147,191,79,83,81,20,199,63,231,246,245,165,68,168,3,193,201,63,64,99,66,4,139,128,113,100,241,71,88,40,88, + 162,46,36,242,106,58,233,0,127,132,49,93,173,12,13,137,160,169,105,2,196,133,196,168,131,131,137,16,23,69,172,147, + 58,176,184,152,247,76,244,93,222,61,14,198,218,22,140,65,191,227,57,159,156,123,190,223,123,175,168,42,255,35,243,167, + 70,126,58,159,11,130,32,253,79,3,10,133,137,193,20,242,56,142,227,212,129,7,20,10,19,131,105,63,243,84,85,179, + 173,245,161,224,94,112,122,118,121,172,147,247,46,77,79,134,64,198,243,188,175,168,222,73,251,153,235,51,51,51,217,202, + 221,74,210,10,58,167,239,141,104,53,23,60,236,223,168,76,126,105,110,32,72,166,124,187,236,205,207,205,31,86,152,43, + 149,74,217,209,145,51,120,158,7,64,110,118,169,56,114,109,233,232,230,194,213,39,10,235,104,124,179,205,130,162,244,245, + 29,225,248,177,19,168,170,12,156,60,69,38,211,133,136,0,32,66,111,34,148,1,68,101,69,144,209,61,25,188,217,122, + 205,187,198,91,0,26,141,109,182,183,183,176,214,2,144,114,44,2,99,34,136,26,247,10,116,72,4,105,102,0,16,134, + 97,115,101,187,107,81,126,191,141,93,163,137,168,164,0,172,209,196,79,164,45,120,3,16,70,33,97,20,162,170,216,216, + 98,99,219,4,4,83,64,244,153,42,234,37,169,1,96,83,91,78,48,0,81,20,177,179,179,3,192,135,79,31,137,109, + 220,26,191,167,164,111,252,132,245,130,194,139,182,107,84,85,173,215,235,137,136,88,35,102,113,117,117,229,242,248,197,241, + 238,95,192,203,133,43,183,0,134,139,247,71,129,60,98,250,59,45,244,168,106,183,115,174,123,121,233,65,209,126,79,206, + 173,61,90,139,156,115,109,94,93,226,134,65,139,27,149,233,207,173,117,217,239,51,77,77,77,157,197,184,245,67,93,61, + 189,213,106,245,219,30,160,51,196,78,213,106,181,231,56,115,222,247,253,100,191,254,95,55,56,136,126,0,228,148,200,42, + 201,231,90,24,0,0,0,0,73,69,78,68,174,66,96,130, +}; + +const uint8_t cheatEditor[937] = { + 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, + 97,0,0,0,6,98,75,71,68,0,255,0,255,0,255,160,189,167,147,0,0,0,9,112,72,89,115,0,0,11,19,0, + 0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,11,28,23,56,34,56,174,182,224,0,0,3,54,73, + 68,65,84,56,203,93,147,75,76,92,117,20,198,191,255,99,230,222,121,128,115,65,96,102,128,162,19,4,154,73,83,19, + 23,197,87,221,24,74,210,141,169,85,27,31,171,54,193,84,107,213,210,102,72,67,26,98,162,13,148,88,155,52,172,76, + 136,143,133,27,92,17,18,187,168,166,52,93,24,181,117,12,29,168,52,242,152,41,101,24,198,14,51,112,231,206,253,255, + 143,27,192,73,191,213,57,223,201,247,203,73,78,14,195,182,70,70,47,246,2,248,206,52,205,135,167,62,248,56,142,42, + 141,140,94,28,147,82,190,245,201,233,254,186,106,127,122,28,239,241,157,70,8,49,241,225,201,211,150,109,219,139,87,199, + 174,12,238,248,151,191,26,205,28,234,233,237,3,112,231,177,240,203,0,190,222,5,40,165,204,123,243,179,252,196,241,190, + 30,165,212,126,0,24,190,244,197,43,209,230,104,164,88,218,96,90,235,238,170,112,59,227,98,146,49,225,217,5,112,206, + 75,11,11,255,192,52,12,86,46,151,227,87,199,174,12,74,41,175,197,98,49,172,60,88,209,90,235,249,237,176,197,133, + 184,222,209,253,105,80,120,106,84,245,6,61,169,84,10,203,233,37,118,226,120,95,167,109,219,239,119,117,117,121,24,56, + 230,239,207,3,192,15,211,227,240,112,33,166,90,227,71,155,234,90,14,49,0,216,5,156,235,31,184,197,57,95,156,155, + 155,133,105,152,204,10,89,81,210,64,118,109,85,3,152,60,123,38,241,25,23,98,188,190,249,192,254,104,199,155,30,80, + 9,68,244,63,0,0,180,214,71,102,238,206,208,234,234,10,92,229,98,99,163,128,100,50,233,0,24,186,249,13,63,239, + 15,181,189,22,123,238,148,233,22,39,161,221,13,0,143,1,206,158,73,252,198,57,255,118,118,46,165,67,161,16,50,15, + 50,68,68,227,207,215,39,158,242,120,107,207,119,118,15,248,221,210,52,42,91,139,208,202,6,136,192,170,1,19,225,240, + 168,75,116,210,205,231,77,184,46,158,14,55,33,77,122,51,120,208,246,190,112,105,64,74,190,134,98,238,6,12,211,132, + 55,244,6,110,255,148,80,2,0,38,34,145,193,35,149,202,245,78,203,234,14,9,33,149,114,169,236,58,168,11,212,176, + 70,211,240,24,203,38,255,117,120,10,158,198,28,178,91,119,17,106,108,2,247,182,99,245,254,207,196,127,140,70,223,117, + 114,185,161,206,112,88,207,44,45,93,43,108,109,58,249,194,6,247,29,214,45,66,50,252,107,151,80,136,153,104,111,168, + 71,50,49,135,252,148,68,197,113,160,221,50,8,4,238,58,206,240,190,214,86,74,45,47,95,56,166,84,111,173,233,83, + 194,231,83,141,175,243,239,255,206,172,32,24,12,32,254,101,3,242,47,173,185,123,163,17,42,222,240,66,85,42,112,221, + 77,128,8,28,229,114,189,227,56,234,152,82,159,79,6,2,151,61,82,154,194,66,193,111,181,31,8,61,187,7,126,146, + 184,55,244,23,204,87,183,36,24,49,93,208,112,29,7,170,178,9,128,32,225,245,102,130,126,255,158,219,109,109,101,173, + 181,156,89,75,23,155,47,60,225,111,217,123,212,8,156,251,5,201,143,254,64,199,157,48,42,191,215,64,107,130,172,149, + 80,218,221,6,0,226,109,203,202,165,215,215,95,92,119,28,153,103,196,194,253,90,60,115,248,29,131,115,15,140,134,39, + 97,68,235,144,94,200,34,155,45,224,145,87,33,62,114,16,190,72,12,68,12,143,30,254,73,172,234,65,246,1,184,197, + 152,52,133,244,59,0,1,68,219,83,218,61,53,237,212,68,80,202,246,253,7,17,166,115,66,199,238,239,170,0,0,0, + 0,73,69,78,68,174,66,96,130, }; const uint8_t folder[1176] = { @@ -136,6 +168,142 @@ const uint8_t game[1490] = { 63,107,9,247,71,127,0,0,0,0,73,69,78,68,174,66,96,130, }; +const uint8_t home[606] = { + 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, + 97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0, + 0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,10,14,20,37,19,83,42,210,59,0,0,1,235,73, + 68,65,84,56,203,149,147,191,107,83,81,20,128,191,123,251,222,75,211,64,242,36,160,85,135,100,81,123,19,104,85,172, + 17,92,28,140,66,19,167,135,212,74,39,145,162,163,24,92,58,180,110,193,169,254,24,234,212,37,139,245,199,96,19,240, + 15,240,63,16,121,91,92,196,90,219,240,98,81,137,33,121,215,33,246,217,151,80,33,103,186,92,206,247,221,115,14,231, + 194,1,225,41,85,172,128,174,128,246,148,122,200,48,177,7,123,74,105,79,169,61,73,113,104,120,177,56,163,203,55,111, + 252,87,34,250,225,154,235,110,20,148,98,253,220,89,182,39,78,113,228,240,56,39,188,38,83,107,107,212,92,151,130,82, + 215,14,185,110,117,64,176,31,126,83,156,97,247,248,49,148,202,50,22,141,210,106,181,24,219,252,74,182,92,30,144,136, + 126,184,122,251,22,157,100,146,116,42,77,42,149,2,64,107,104,183,219,236,214,235,156,44,149,66,18,177,31,126,247,160, + 68,199,48,201,229,46,96,219,118,208,154,214,26,0,223,247,249,185,181,69,114,110,46,144,136,10,232,130,82,188,127,84, + 198,107,54,57,63,157,35,30,143,35,132,8,9,124,223,15,206,134,16,140,230,243,212,92,23,3,184,3,172,78,157,62, + 195,253,210,61,54,170,111,3,240,201,202,51,86,30,63,69,139,17,116,167,133,48,70,209,221,223,44,47,45,209,238,165, + 204,202,121,120,190,48,153,33,17,79,0,160,212,4,153,140,34,155,205,244,94,20,146,145,244,149,222,196,83,121,144,22, + 90,107,22,38,51,204,195,186,236,239,211,178,44,76,211,196,48,140,224,254,75,227,7,0,31,234,59,116,187,126,144,11, + 48,32,144,82,34,165,196,146,159,1,232,96,241,241,83,3,128,111,222,47,58,34,18,90,36,99,96,179,254,14,207,231, + 40,0,151,46,78,115,53,26,67,234,113,46,75,147,237,77,66,21,24,253,21,252,155,126,239,165,239,222,14,162,217,56, + 112,245,3,65,36,18,193,182,109,18,137,4,2,137,16,16,139,197,112,28,103,0,178,44,43,252,23,174,207,58,175,0, + 135,225,98,245,229,139,215,119,255,0,86,248,213,163,133,187,128,26,0,0,0,0,73,69,78,68,174,66,96,130, +}; + +const uint8_t hotkeys[587] = { + 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, + 97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13, + 215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99, + 97,112,101,46,111,114,103,155,238,60,26,0,0,1,200,73,68,65,84,56,141,181,146,177,107,83,81,20,198,127,247,222, + 115,95,95,8,73,159,136,180,209,38,177,21,7,165,21,26,16,204,228,34,102,8,8,34,212,168,127,72,193,191,160,131, + 45,174,29,178,184,180,116,17,212,12,25,4,23,137,32,168,56,85,227,146,162,85,112,145,212,190,151,52,125,185,14,109, + 53,29,164,193,226,7,151,115,46,231,124,223,189,231,240,169,82,169,196,113,160,15,146,122,189,174,142,37,48,191,48,159, + 30,134,80,169,84,252,193,187,58,24,161,88,156,157,242,60,223,3,239,187,136,216,100,210,88,99,196,199,152,139,190,245, + 175,2,87,28,76,3,30,74,189,72,38,82,229,106,181,26,203,31,45,127,195,57,123,201,250,186,108,69,143,59,103,50, + 32,99,86,108,216,239,179,174,148,187,15,242,174,213,106,253,204,159,157,104,110,111,255,40,0,175,127,11,52,26,141,93, + 224,205,254,249,219,247,131,252,228,196,50,142,157,173,173,206,251,67,59,56,10,115,119,230,174,163,226,183,202,169,200,104, + 175,80,171,213,186,0,114,20,113,239,229,91,151,193,61,82,112,111,101,101,237,249,96,77,45,173,45,37,78,118,131,166, + 159,24,57,163,181,70,27,141,86,123,209,104,131,214,10,173,13,206,57,0,246,123,94,61,88,120,88,4,144,157,86,120, + 45,83,184,112,106,102,122,6,107,61,68,4,43,22,99,4,173,13,56,71,223,245,137,162,136,48,10,49,90,211,104,188, + 156,173,84,110,158,95,93,125,220,20,17,239,118,144,62,225,133,97,135,205,175,159,72,37,211,160,0,20,74,41,218,237, + 54,249,92,142,56,142,249,178,249,153,108,54,79,106,52,144,245,15,31,111,0,139,98,180,46,7,65,0,206,145,61,157, + 99,183,31,31,154,127,52,157,166,215,235,1,138,169,201,115,116,186,93,50,99,227,102,196,179,119,129,69,137,227,120,227, + 233,179,39,90,169,225,157,236,156,3,199,55,24,112,226,191,98,104,31,252,55,129,95,252,113,137,228,164,151,154,151,0, + 0,0,0,73,69,78,68,174,66,96,130, +}; + +const uint8_t input[812] = { + 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, + 97,0,0,0,6,98,75,71,68,0,172,0,77,0,0,52,214,215,123,0,0,0,9,112,72,89,115,0,0,11,19,0, + 0,11,19,1,0,154,156,24,0,0,0,7,116,73,77,69,7,213,4,7,15,10,39,178,201,163,153,0,0,0,140,116, + 69,88,116,67,111,109,109,101,110,116,0,77,101,110,117,45,115,105,122,101,100,32,105,99,111,110,10,61,61,61,61,61, + 61,61,61,61,61,10,10,40,99,41,32,50,48,48,51,32,74,97,107,117,98,32,39,106,105,109,109,97,99,39,32,83, + 116,101,105,110,101,114,44,32,10,104,116,116,112,58,47,47,106,105,109,109,97,99,46,109,117,115,105,99,104,97,108,108, + 46,99,122,10,10,99,114,101,97,116,101,100,32,119,105,116,104,32,116,104,101,32,71,73,77,80,44,10,104,116,116,112, + 58,47,47,119,119,119,46,103,105,109,112,46,111,114,103,103,138,199,71,0,0,2,33,73,68,65,84,56,203,149,146,203, + 107,83,65,20,198,127,147,220,228,38,214,210,164,177,54,245,209,135,160,184,73,176,20,255,130,130,123,17,138,136,130,168, + 32,88,8,193,133,173,15,180,221,136,46,68,10,10,130,72,22,93,213,186,19,68,208,133,187,34,84,179,19,68,20,42, + 21,53,53,183,105,244,230,117,111,230,184,184,105,154,66,178,232,129,3,195,240,205,55,191,249,230,40,58,212,199,179,132, + 255,170,221,51,97,41,95,2,124,235,42,178,96,80,191,58,62,95,40,181,234,84,167,195,117,124,223,250,14,237,141,27, + 225,16,162,53,142,93,102,237,251,186,181,161,122,6,79,204,231,237,77,173,175,157,129,70,165,99,67,177,184,175,43,140, + 235,247,99,21,107,216,174,143,129,35,253,189,192,253,86,173,175,195,11,82,129,93,97,252,166,137,17,12,16,12,26,252, + 94,181,136,14,198,137,200,198,153,86,161,145,74,79,206,0,119,90,55,203,86,6,167,84,193,202,217,116,71,187,40,230, + 255,225,214,28,220,170,67,69,153,209,84,250,178,52,164,179,42,149,158,148,185,135,143,182,93,255,235,229,61,170,203,115, + 88,185,34,249,181,18,162,96,223,240,30,162,177,46,234,135,47,112,240,212,109,108,219,230,198,173,41,12,0,173,53,133, + 66,97,11,235,248,57,114,111,30,211,63,212,199,200,104,15,134,25,160,248,243,15,185,85,135,3,231,47,98,89,22,65, + 51,184,149,129,104,241,90,188,86,129,16,3,215,151,88,212,167,249,186,244,133,207,239,62,241,170,52,206,254,155,239,81, + 129,16,34,2,141,71,120,4,162,209,34,32,226,125,172,0,134,137,27,234,101,228,193,42,43,43,43,228,223,190,134,128, + 137,214,26,192,51,105,18,136,96,189,184,198,143,233,97,214,23,167,26,68,26,183,238,182,36,35,72,93,35,186,209,13, + 3,163,153,252,242,2,82,43,81,90,94,160,251,228,93,0,142,37,71,121,250,236,73,115,93,23,221,68,111,230,181,25, + 98,104,108,130,202,135,231,132,198,38,208,90,163,68,72,36,146,36,18,73,68,20,74,137,135,223,48,216,70,96,154,38, + 71,175,100,128,76,219,169,178,109,111,114,203,229,50,0,149,74,101,59,65,54,155,197,117,93,118,82,74,169,45,131,72, + 36,210,68,2,168,86,171,0,212,106,181,142,6,142,227,120,70,237,70,121,7,53,251,31,168,192,0,159,97,230,172,204, + 0,0,0,0,73,69,78,68,174,66,96,130, +}; + +const uint8_t server[408] = { + 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, + 97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,11,18,0, + 0,11,18,1,210,221,126,252,0,0,0,7,116,73,77,69,7,214,2,16,22,3,20,11,54,9,17,0,0,1,37,73, + 68,65,84,56,203,157,146,61,75,195,80,20,134,159,155,92,242,63,164,46,221,2,226,238,32,226,38,82,255,131,17,10, + 34,56,74,196,42,232,228,80,63,210,95,32,4,99,5,255,134,131,110,46,130,56,152,22,170,129,214,170,225,82,18,7, + 13,26,242,97,240,29,207,121,207,203,121,206,189,130,2,181,246,182,61,160,65,185,58,178,164,217,88,152,95,164,86,55, + 115,155,131,167,7,188,174,107,149,5,80,171,155,44,55,47,48,140,180,77,169,9,87,206,10,0,165,1,66,8,12,67, + 98,206,78,3,49,32,0,184,189,190,39,138,190,60,178,140,245,180,189,207,220,20,168,151,30,143,98,134,247,15,197,235, + 56,76,121,100,69,86,122,207,111,223,21,29,128,56,142,127,16,170,176,70,163,97,170,254,123,131,74,172,238,225,82,102, + 179,204,17,117,93,163,63,24,231,178,58,71,7,121,132,118,42,224,230,174,15,177,200,101,93,91,109,166,38,53,77,227, + 196,105,183,100,98,82,106,130,49,28,101,110,144,40,8,130,220,35,203,132,231,242,184,81,248,10,0,238,249,89,97,64, + 199,235,186,214,31,127,222,182,183,118,118,139,2,172,141,245,77,194,48,204,157,76,88,129,194,0,124,223,231,191,146,101, + 124,85,244,9,241,192,132,130,214,14,135,66,0,0,0,0,73,69,78,68,174,66,96,130, +}; + +const uint8_t stateManager[378] = { + 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, + 97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0, + 0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,6,16,18,41,48,44,67,93,90,0,0,1,7,73, + 68,65,84,56,203,189,146,189,74,3,65,28,196,127,251,145,69,177,176,73,44,124,128,128,207,225,19,8,130,136,31,129, + 136,104,37,34,104,173,32,10,130,181,165,112,6,242,20,190,78,138,120,8,90,104,178,183,123,107,17,15,205,37,94,238, + 82,56,229,252,255,59,179,179,179,226,168,221,4,216,19,130,136,10,8,129,125,224,73,103,135,175,78,46,73,146,1,225, + 123,90,4,33,36,215,15,183,81,8,32,142,15,154,225,188,125,138,148,211,151,245,48,198,153,250,4,175,134,49,55,157, + 14,58,35,30,163,59,250,214,148,186,254,138,177,180,118,47,70,6,25,217,183,134,237,213,151,82,2,221,94,131,193,231, + 59,90,215,144,204,9,169,70,222,114,150,83,183,215,40,20,210,191,115,253,181,156,231,151,148,35,245,14,231,146,31,129, + 245,250,219,92,81,52,128,181,31,60,199,203,149,90,216,202,71,168,218,130,79,253,236,71,44,213,6,64,234,221,255,182, + 48,33,96,236,43,155,27,135,99,131,212,123,164,82,164,222,231,62,208,56,39,90,59,107,247,139,11,254,76,235,218,84, + 7,231,146,194,8,95,134,90,101,183,231,143,210,134,0,0,0,0,73,69,78,68,174,66,96,130, +}; + +const uint8_t timing[897] = { + 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, + 97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0, + 0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,9,15,8,58,5,128,132,46,85,0,0,3,14,73, + 68,65,84,56,203,101,146,75,76,92,101,28,197,127,223,119,239,188,152,185,119,96,34,8,210,202,35,88,29,108,48,150, + 190,226,162,177,59,220,152,138,193,186,32,26,54,38,68,87,181,53,38,44,77,99,26,31,137,198,68,77,172,53,177,139, + 62,240,17,77,76,163,46,176,5,77,85,40,5,58,88,43,218,17,233,76,167,204,12,195,204,189,195,12,119,190,207,141, + 109,40,158,228,236,206,57,255,71,142,96,19,142,28,24,25,210,146,23,5,226,97,148,14,8,67,122,90,138,27,85,181, + 126,230,157,207,94,127,101,179,94,108,48,246,104,248,116,247,190,71,123,182,247,198,9,71,194,8,1,90,67,113,181,196, + 204,84,130,75,151,230,211,131,195,231,142,245,60,146,10,34,117,146,80,242,180,185,193,124,97,112,120,192,46,27,138,85, + 159,131,40,190,118,103,138,221,113,140,120,224,65,26,59,83,205,141,205,149,55,61,189,115,213,208,191,172,8,183,93,73, + 0,141,62,62,56,60,96,223,88,43,208,254,80,136,104,245,237,187,214,180,235,45,182,110,107,161,119,111,142,104,195,46, + 131,192,187,13,200,167,163,192,14,121,228,192,200,208,238,125,59,118,150,180,71,91,87,35,45,91,226,155,207,68,74,137, + 16,2,51,16,67,178,68,165,116,2,167,52,19,4,253,131,4,6,182,247,198,89,46,103,105,109,109,166,144,255,135,248, + 227,167,72,103,125,119,63,75,8,42,94,31,158,232,39,149,172,114,242,195,7,170,120,242,156,169,209,123,194,145,48,150, + 23,66,41,133,207,103,81,118,139,132,234,44,70,94,218,134,223,222,203,115,109,57,108,219,66,200,24,210,127,16,195,187, + 201,194,212,199,209,161,23,250,131,18,168,23,2,154,154,26,200,23,242,40,165,168,41,141,227,20,57,250,222,239,68,98, + 221,76,76,76,160,148,66,107,133,97,26,152,134,4,165,5,208,101,2,43,90,19,43,187,85,58,58,218,169,84,214,80, + 74,81,44,22,233,238,238,230,229,195,175,114,237,218,85,106,181,26,82,74,180,82,104,13,72,52,176,32,5,226,162,83, + 114,88,252,59,141,82,10,207,243,240,60,143,67,135,14,147,72,36,200,100,210,88,150,69,173,86,195,178,108,202,110,25, + 167,228,128,207,112,63,57,253,150,35,129,179,115,147,243,216,134,133,83,114,241,249,252,140,141,141,49,58,58,74,42,181, + 116,39,48,28,142,160,53,44,95,207,50,55,57,79,133,218,12,128,124,227,203,163,39,126,62,63,53,217,24,182,25,255, + 254,87,180,214,244,245,61,65,58,157,98,173,82,65,107,77,32,16,192,52,77,242,153,28,238,178,203,197,31,167,171,95, + 77,156,121,6,192,0,232,188,183,107,110,97,54,249,236,254,253,143,249,255,252,99,145,92,33,207,253,109,91,8,6,130, + 4,131,33,252,190,0,55,255,202,144,191,190,194,201,15,206,234,217,165,217,247,167,230,127,154,3,178,6,192,76,114,58, + 239,55,253,87,150,174,102,246,52,53,196,162,109,45,173,120,69,143,74,126,141,114,182,204,173,228,45,46,143,207,242,245, + 231,223,174,143,255,118,225,248,249,233,239,190,1,210,64,78,108,232,74,61,208,249,228,174,254,231,239,187,103,235,83,117, + 254,186,22,41,164,169,209,202,93,119,139,233,66,58,241,197,248,169,143,128,20,112,5,88,4,180,224,255,136,252,23,118, + 155,26,40,3,171,64,22,40,0,234,182,248,95,201,36,100,6,22,194,54,223,0,0,0,0,73,69,78,68,174,66,96, + 130, +}; + const uint8_t unverified[1675] = { 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122, 244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,6,66,73,68,65,84,88,133,229,151,217,115,84, @@ -192,4 +360,52 @@ const uint8_t unverified[1675] = { 0,0,0,73,69,78,68,174,66,96,130, }; +const uint8_t up[652] = { + 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, + 97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114, + 101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,30,73,68,65,84,56,141, + 149,147,79,104,19,65,20,198,191,55,187,51,217,141,133,122,104,76,255,209,130,4,237,193,64,42,168,208,213,138,104,68, + 98,69,45,20,114,107,74,201,73,144,98,22,193,171,199,82,145,160,23,15,69,79,30,165,66,241,226,77,42,94,68,80, + 208,67,41,180,42,149,148,52,137,166,154,38,217,217,25,15,81,172,33,169,246,29,223,251,230,199,124,223,155,33,173,53, + 218,213,201,155,124,30,0,150,102,189,233,118,26,214,110,224,184,60,221,223,21,73,246,117,29,76,58,46,79,239,9,224, + 184,60,26,16,251,178,87,206,76,6,199,70,147,193,128,8,102,29,151,71,255,11,224,184,188,3,132,197,137,115,211,214, + 150,151,199,150,183,137,248,200,37,11,132,69,199,229,29,255,4,16,225,209,232,112,162,167,55,52,72,159,75,31,240,177, + 244,30,157,157,157,20,27,58,209,13,194,195,93,1,142,203,211,253,7,34,137,179,71,199,249,74,254,13,12,198,97,50, + 142,87,107,11,136,13,29,23,161,253,61,137,230,60,216,142,195,81,193,237,108,42,225,218,235,229,101,104,242,32,132,137, + 58,125,199,54,21,241,46,255,28,23,78,95,13,154,166,248,43,15,182,211,247,212,88,198,82,228,161,34,75,16,1,1, + 30,224,40,203,117,8,219,64,65,174,162,168,86,113,254,212,69,139,216,159,60,216,111,223,241,99,227,221,135,250,98,148, + 175,172,193,228,6,56,55,80,81,5,84,141,175,16,54,131,176,25,150,191,189,64,184,55,68,71,14,15,135,137,53,242, + 160,145,140,153,30,232,142,100,111,76,220,177,5,15,64,65,194,135,135,215,185,5,188,45,60,131,100,21,16,35,36,6, + 50,208,90,67,41,160,94,175,225,241,211,249,74,177,180,57,99,18,67,234,83,110,197,158,185,119,185,225,137,163,122,247, + 250,19,171,44,115,240,141,109,112,193,192,76,130,193,9,115,15,110,87,101,77,91,191,236,7,137,33,101,46,205,122,78, + 211,38,180,214,10,165,250,23,48,131,96,112,6,30,104,64,100,77,91,47,231,60,218,245,29,0,128,210,62,126,212,139, + 141,43,251,26,178,174,160,85,235,63,99,182,106,250,74,162,90,219,134,50,53,160,21,124,73,240,189,61,0,164,47,17, + 31,188,6,98,0,49,2,17,246,0,32,108,220,186,63,25,110,37,38,194,70,115,239,39,48,247,197,219,182,208,154,34, + 0,0,0,0,73,69,78,68,174,66,96,130, +}; + +const uint8_t video[662] = { + 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255, + 97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13, + 215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99, + 97,112,101,46,111,114,103,155,238,60,26,0,0,2,19,73,68,65,84,56,141,165,147,203,106,20,65,20,134,191,83,85, + 115,109,72,50,32,6,98,18,35,38,11,183,222,54,186,112,99,6,29,240,25,124,4,55,130,184,81,80,4,241,49,124, + 131,108,92,8,9,17,34,226,222,96,188,129,38,16,131,97,72,156,233,234,233,158,238,58,46,58,17,19,3,65,60,155, + 42,170,234,255,207,119,206,161,164,221,110,243,63,225,58,157,206,29,17,121,168,170,181,127,17,138,136,87,213,187,78,85, + 31,183,111,92,143,154,141,38,121,174,199,103,116,130,181,134,110,183,91,95,90,124,245,196,169,106,109,116,116,132,249,155, + 207,73,233,3,160,122,132,209,222,81,77,34,94,190,184,205,200,232,40,64,195,237,223,167,161,207,133,171,99,136,200,30, + 34,128,148,171,0,90,238,223,44,111,179,250,238,61,83,83,147,37,81,154,166,128,160,226,136,211,80,10,20,68,228,128, + 73,54,204,217,238,254,4,28,103,103,103,49,18,74,3,239,61,33,20,24,99,24,228,82,102,43,155,4,148,194,205,31, + 59,196,62,5,160,97,90,124,88,91,99,122,122,146,36,73,112,73,146,80,20,1,140,35,201,203,108,138,32,64,156,164, + 236,244,18,84,29,149,250,94,181,137,101,122,230,52,21,103,240,222,227,226,56,70,131,98,76,133,65,112,251,0,36,233, + 144,108,104,112,181,232,224,248,172,229,243,199,79,204,205,205,210,239,247,75,2,69,17,107,73,131,3,5,69,81,107,169, + 216,35,134,97,135,76,156,154,192,24,202,18,188,247,0,24,227,176,213,232,111,197,161,24,154,62,69,81,160,8,113,28, + 227,66,40,187,217,106,56,178,205,193,177,6,81,213,33,70,49,34,37,129,115,174,183,254,109,163,245,236,209,249,99,197, + 191,41,210,156,245,245,13,128,29,233,116,58,183,106,181,218,3,17,141,64,12,48,83,169,56,87,169,86,197,26,3,64, + 17,2,89,150,105,62,204,135,192,87,208,160,202,174,247,201,125,151,231,249,66,158,231,11,0,170,26,181,78,140,189,157, + 111,183,207,93,186,120,153,241,147,227,168,42,91,91,223,89,121,189,162,75,203,75,171,73,111,112,37,203,50,191,79,35, + 135,191,179,49,102,164,222,172,63,109,70,205,107,214,154,51,128,22,161,248,50,240,131,197,222,110,255,158,136,196,127,190, + 255,5,119,143,242,70,185,147,13,30,0,0,0,0,73,69,78,68,174,66,96,130, +}; + }; diff --git a/target-ethos/resource/resource.hpp b/target-ethos/resource/resource.hpp index 5d908b05..8a518946 100644 --- a/target-ethos/resource/resource.hpp +++ b/target-ethos/resource/resource.hpp @@ -1,7 +1,16 @@ namespace resource { - extern const uint8_t home[606]; - extern const uint8_t up[652]; + extern const uint8_t advanced[611]; + extern const uint8_t audio[592]; + extern const uint8_t cheatEditor[937]; extern const uint8_t folder[1176]; extern const uint8_t game[1490]; + extern const uint8_t home[606]; + extern const uint8_t hotkeys[587]; + extern const uint8_t input[812]; + extern const uint8_t server[408]; + extern const uint8_t stateManager[378]; + extern const uint8_t timing[897]; extern const uint8_t unverified[1675]; + extern const uint8_t up[652]; + extern const uint8_t video[662]; }; diff --git a/target-ethos/resource/server.png b/target-ethos/resource/server.png new file mode 100644 index 00000000..271d37d0 Binary files /dev/null and b/target-ethos/resource/server.png differ diff --git a/target-ethos/resource/state-manager.png b/target-ethos/resource/state-manager.png new file mode 100644 index 00000000..60cade46 Binary files /dev/null and b/target-ethos/resource/state-manager.png differ diff --git a/target-ethos/resource/timing.png b/target-ethos/resource/timing.png new file mode 100644 index 00000000..18b7c678 Binary files /dev/null and b/target-ethos/resource/timing.png differ diff --git a/target-ethos/resource/video.png b/target-ethos/resource/video.png new file mode 100644 index 00000000..226881fd Binary files /dev/null and b/target-ethos/resource/video.png differ diff --git a/target-ethos/settings/advanced.cpp b/target-ethos/settings/advanced.cpp index 33a103ba..947b8473 100644 --- a/target-ethos/settings/advanced.cpp +++ b/target-ethos/settings/advanced.cpp @@ -1,12 +1,12 @@ AdvancedSettings* advancedSettings = nullptr; AdvancedSettings::AdvancedSettings() { - driverTitle.setFont(program->titleFont); + driverTitle.setFont(program->boldFont); driverTitle.setText("Driver Selection"); videoLabel.setText("Video:"); audioLabel.setText("Audio:"); inputLabel.setText("Input:"); - libraryTitle.setFont(program->titleFont); + libraryTitle.setFont(program->boldFont); libraryTitle.setText("Game Library Path"); libraryLabel.setText("Path:"); libraryPath.setEditable(false); @@ -44,7 +44,7 @@ AdvancedSettings::AdvancedSettings() { if(list[n] == config->input.driver) inputDriver.setSelection(n); } - append(driverTitle, {~0, 0}, 5); + append(driverTitle, {~0, 0}); append(driverLayout, {~0, 0}, 15); driverLayout.append(videoLabel, {0, 0}, 5); driverLayout.append(videoDriver, {~0, 0}, 5); @@ -52,7 +52,7 @@ AdvancedSettings::AdvancedSettings() { driverLayout.append(audioDriver, {~0, 0}, 5); driverLayout.append(inputLabel, {0, 0}, 5); driverLayout.append(inputDriver, {~0, 0}); - append(libraryTitle, {~0, 0}, 5); + append(libraryTitle, {~0, 0}); append(libraryLayout, {~0, 0}, 15); libraryLayout.append(libraryLabel, {0, 0}, 5); libraryLayout.append(libraryPath, {~0, 0}, 5); diff --git a/target-ethos/settings/audio.cpp b/target-ethos/settings/audio.cpp index 3465d5ff..6059576c 100644 --- a/target-ethos/settings/audio.cpp +++ b/target-ethos/settings/audio.cpp @@ -7,18 +7,24 @@ AudioSlider::AudioSlider() { } AudioSettings::AudioSettings() { - title.setFont(program->titleFont); - title.setText("Audio Settings"); frequencyLabel.setText("Frequency:"); - frequency.append("32000hz", "44100hz", "48000hz", "96000hz"); + frequency.append("32000hz"); + frequency.append("44100hz"); + frequency.append("48000hz"); + frequency.append("96000hz"); latencyLabel.setText("Latency:"); - latency.append("20ms", "40ms", "60ms", "80ms", "100ms"); + latency.append("20ms"); + latency.append("40ms"); + latency.append("60ms"); + latency.append("80ms"); + latency.append("100ms"); resamplerLabel.setText("Resampler:"); - resampler.append("Linear", "Hermite", "Sinc"); + resampler.append("Linear"); + resampler.append("Hermite"); + resampler.append("Sinc"); volume.name.setText("Volume:"); volume.slider.setLength(201); - append(title, {~0, 0}, 5); append(controlLayout, {~0, 0}, 5); controlLayout.append(frequencyLabel, {0, 0}, 5); controlLayout.append(frequency, {~0, 0}, 5); diff --git a/target-ethos/settings/audio.hpp b/target-ethos/settings/audio.hpp index 04a1e80a..d27fcfc8 100644 --- a/target-ethos/settings/audio.hpp +++ b/target-ethos/settings/audio.hpp @@ -7,7 +7,6 @@ struct AudioSlider : HorizontalLayout { }; struct AudioSettings : SettingsLayout { - Label title; HorizontalLayout controlLayout; Label frequencyLabel; ComboButton frequency; diff --git a/target-ethos/settings/hotkey.cpp b/target-ethos/settings/hotkey.cpp index e88c51a5..44c32979 100644 --- a/target-ethos/settings/hotkey.cpp +++ b/target-ethos/settings/hotkey.cpp @@ -1,14 +1,10 @@ HotkeySettings* hotkeySettings = nullptr; HotkeySettings::HotkeySettings() { - title.setFont(program->titleFont); - title.setText("Hotkey Bindings"); - - inputList.setHeaderText("Name", "Mapping"); + inputList.setHeaderText({"Name", "Mapping"}); inputList.setHeaderVisible(); eraseButton.setText("Erase"); - append(title, {~0, 0}, 5); append(inputList, {~0, ~0}, 5); append(controlLayout, {~0, 0}); controlLayout.append(spacer, {~0, 0}); @@ -18,7 +14,7 @@ HotkeySettings::HotkeySettings() { inputList.onActivate = {&HotkeySettings::assignInput, this}; eraseButton.onActivate = {&HotkeySettings::eraseInput, this}; - for(auto& hotkey : inputManager->hotkeyMap) inputList.append("", ""); + for(auto& hotkey : inputManager->hotkeyMap) inputList.append({"", ""}); refresh(); } @@ -33,7 +29,7 @@ void HotkeySettings::refresh() { mapping.replace("KB0::", ""); mapping.replace("MS0::", "Mouse::"); mapping.replace(",", " and "); - inputList.modify(index++, hotkey->name, mapping); + inputList.setText(index++, {hotkey->name, mapping}); } synchronize(); } diff --git a/target-ethos/settings/hotkey.hpp b/target-ethos/settings/hotkey.hpp index da076641..ac187537 100644 --- a/target-ethos/settings/hotkey.hpp +++ b/target-ethos/settings/hotkey.hpp @@ -1,5 +1,4 @@ struct HotkeySettings : SettingsLayout { - Label title; ListView inputList; HorizontalLayout controlLayout; Widget spacer; diff --git a/target-ethos/settings/input.cpp b/target-ethos/settings/input.cpp index 3f4f7f31..3aa0a6a3 100644 --- a/target-ethos/settings/input.cpp +++ b/target-ethos/settings/input.cpp @@ -1,17 +1,14 @@ InputSettings* inputSettings = nullptr; InputSettings::InputSettings() { - title.setFont(program->titleFont); - title.setText("Input Settings"); focusLabel.setText("When Focus is Lost:"); focusPause.setText("Pause Emulation"); focusAllow.setText("Allow Input"); - inputList.setHeaderText("Name", "Mapping"); + inputList.setHeaderText({"Name", "Mapping"}); inputList.setHeaderVisible(); resetButton.setText("Reset"); eraseButton.setText("Erase"); - append(title, {~0, 0}, 5); append(focusLayout, {~0, 0}, 5); focusLayout.append(focusLabel, {0, 0}, 5); focusLayout.append(focusPause, {0, 0}, 5); @@ -115,7 +112,7 @@ void InputSettings::portChanged() { void InputSettings::deviceChanged() { inputList.reset(); - for(unsigned number : activeDevice().order) inputList.append("", ""); + for(unsigned number : activeDevice().order) inputList.append({"", ""}); inputChanged(); synchronize(); } @@ -129,7 +126,7 @@ void InputSettings::inputChanged() { mapping.replace("KB0::", ""); mapping.replace("MS0::", "Mouse::"); mapping.replace(",", " or "); - inputList.modify(index++, input.name, mapping); + inputList.setText(index++, {input.name, mapping}); } } diff --git a/target-ethos/settings/input.hpp b/target-ethos/settings/input.hpp index 8bd824bd..39ab4348 100644 --- a/target-ethos/settings/input.hpp +++ b/target-ethos/settings/input.hpp @@ -1,9 +1,8 @@ struct InputSettings : SettingsLayout { - Label title; HorizontalLayout focusLayout; Label focusLabel; - CheckButton focusPause; - CheckButton focusAllow; + CheckLabel focusPause; + CheckLabel focusAllow; HorizontalLayout selectionLayout; ComboButton systemList; ComboButton portList; diff --git a/target-ethos/settings/server.cpp b/target-ethos/settings/server.cpp index a8d7e9e5..44e4fd1a 100644 --- a/target-ethos/settings/server.cpp +++ b/target-ethos/settings/server.cpp @@ -1,8 +1,6 @@ ServerSettings* serverSettings = nullptr; ServerSettings::ServerSettings() { - title.setFont(program->titleFont); - title.setText("Server Settings"); hostLabel.setText("Hostname:"); userLabel.setText("Username:"); passLabel.setText("Password:"); @@ -12,7 +10,6 @@ ServerSettings::ServerSettings() { Font::size(program->normalFont, "Username:").width ); - append(title, {~0, 0}, 5); append(hostLayout, {~0, 0}, 5); hostLayout.append(hostLabel, {width, 0}, 5); hostLayout.append(hostEdit, {~0, 0}); diff --git a/target-ethos/settings/server.hpp b/target-ethos/settings/server.hpp index 888b5f2f..617c49c2 100644 --- a/target-ethos/settings/server.hpp +++ b/target-ethos/settings/server.hpp @@ -1,5 +1,4 @@ struct ServerSettings : SettingsLayout { - Label title; HorizontalLayout hostLayout; Label hostLabel; LineEdit hostEdit; diff --git a/target-ethos/settings/settings.cpp b/target-ethos/settings/settings.cpp index dc646b30..daf77073 100644 --- a/target-ethos/settings/settings.cpp +++ b/target-ethos/settings/settings.cpp @@ -6,16 +6,10 @@ #include "timing.cpp" #include "server.cpp" #include "advanced.cpp" -Settings *settings = nullptr; - -void SettingsLayout::append(Sizable& sizable, const Size& size, unsigned spacing) { - layout.append(sizable, size, spacing); -} +Settings* settings = nullptr; SettingsLayout::SettingsLayout() { setMargin(5); - HorizontalLayout::append(spacer, {120, ~0}, 5); - HorizontalLayout::append(layout, { ~0, ~0}); } Settings::Settings() { @@ -26,54 +20,27 @@ Settings::Settings() { setStatusVisible(); layout.setMargin(5); - panelList.setFont(program->boldFont); - panelList.append("Video"); - panelList.append("Audio"); - panelList.append("Input"); - panelList.append("Hotkeys"); - panelList.append("Timing"); - panelList.append("Server"); - panelList.append("Advanced"); + panels.append("Video", {resource::video, sizeof resource::video}); + panels.append("Audio", {resource::audio, sizeof resource::audio}); + panels.append("Input", {resource::input, sizeof resource::input}); + panels.append("Hotkeys", {resource::hotkeys, sizeof resource::hotkeys}); + panels.append("Timing", {resource::timing, sizeof resource::timing}); + panels.append("Server", {resource::server, sizeof resource::server}); + panels.append("Advanced", {resource::advanced, sizeof resource::advanced}); + panels.setLayout(0, *videoSettings); + panels.setLayout(1, *audioSettings); + panels.setLayout(2, *inputSettings); + panels.setLayout(3, *hotkeySettings); + panels.setLayout(4, *timingSettings); + panels.setLayout(5, *serverSettings); + panels.setLayout(6, *advancedSettings); + panels.setSelection(2); append(layout); - layout.append(panelList, {120, ~0}, 5); - append(*videoSettings); - append(*audioSettings); - append(*inputSettings); - append(*hotkeySettings); - append(*timingSettings); - append(*serverSettings); - append(*advancedSettings); + layout.append(panels, {~0, ~0}); onClose = [&] { timingSettings->analysis.stop = true; setVisible(false); }; - - panelList.onChange = {&Settings::panelChanged, this}; - - panelList.setSelection(2); - panelChanged(); -} - -void Settings::panelChanged() { - setStatusText(""); - videoSettings->setVisible(false); - audioSettings->setVisible(false); - inputSettings->setVisible(false); - hotkeySettings->setVisible(false); - timingSettings->setVisible(false); - serverSettings->setVisible(false); - advancedSettings->setVisible(false); - if(panelList.selected() == false) return; - - switch(panelList.selection()) { - case 0: return videoSettings->setVisible(); - case 1: return audioSettings->setVisible(); - case 2: return inputSettings->setVisible(); - case 3: return hotkeySettings->setVisible(); - case 4: return timingSettings->setVisible(); - case 5: return serverSettings->setVisible(); - case 6: return advancedSettings->setVisible(); - } } diff --git a/target-ethos/settings/settings.hpp b/target-ethos/settings/settings.hpp index 8e28ceb7..db3e8235 100644 --- a/target-ethos/settings/settings.hpp +++ b/target-ethos/settings/settings.hpp @@ -1,8 +1,4 @@ -struct SettingsLayout : HorizontalLayout { - Widget spacer; - VerticalLayout layout; - - void append(Sizable& widget, const Size &size, unsigned spacing = 0); +struct SettingsLayout : VerticalLayout { SettingsLayout(); }; @@ -15,10 +11,9 @@ struct SettingsLayout : HorizontalLayout { #include "advanced.hpp" struct Settings : Window { - HorizontalLayout layout; - ListView panelList; + VerticalLayout layout; + TabFrame panels; - void panelChanged(); Settings(); }; diff --git a/target-ethos/settings/timing.cpp b/target-ethos/settings/timing.cpp index 3730ccbd..3fdc0c36 100644 --- a/target-ethos/settings/timing.cpp +++ b/target-ethos/settings/timing.cpp @@ -16,14 +16,11 @@ TimingAdjustment::TimingAdjustment() { } TimingSettings::TimingSettings() { - title.setFont(program->titleFont); - title.setText("Audiovisual Synchronization"); videoAdjust.name.setText("Video:"); videoAdjust.value.setText({config->timing.video}); audioAdjust.name.setText("Audio:"); audioAdjust.value.setText({config->timing.audio}); - append(title, {~0, 0}, 5); append(videoAdjust, {~0, 0}, 5); append(audioAdjust, {~0, 0}, 5); @@ -78,7 +75,7 @@ void TimingSettings::analyzeAudioFrequency() { void TimingSettings::analyzeStart() { audio.clear(); - settings->panelList.setEnabled(false); +//settings->panels.setEnabled(false); videoAdjust.analyze.setEnabled(false); audioAdjust.analyze.setEnabled(false); settings->setStatusText("Initializing ..."); @@ -121,7 +118,7 @@ void TimingSettings::analyzeStop() { video.set(Video::Synchronize, config->video.synchronize); audio.set(Audio::Synchronize, config->audio.synchronize); - settings->panelList.setEnabled(true); +//settings->panels.setEnabled(true); videoAdjust.analyze.setEnabled(true); audioAdjust.analyze.setEnabled(true); videoAdjust.stop.setEnabled(false); diff --git a/target-ethos/settings/timing.hpp b/target-ethos/settings/timing.hpp index 81bf00d6..8c89cc9b 100644 --- a/target-ethos/settings/timing.hpp +++ b/target-ethos/settings/timing.hpp @@ -10,7 +10,6 @@ struct TimingAdjustment : HorizontalLayout { }; struct TimingSettings : SettingsLayout { - Label title; TimingAdjustment videoAdjust; TimingAdjustment audioAdjust; diff --git a/target-ethos/settings/video.cpp b/target-ethos/settings/video.cpp index 5a1b4aee..e9d293ad 100644 --- a/target-ethos/settings/video.cpp +++ b/target-ethos/settings/video.cpp @@ -7,8 +7,6 @@ VideoSlider::VideoSlider() { } VideoSettings::VideoSettings() { - title.setFont(program->titleFont); - title.setText("Video Settings"); colorAdjustment.setFont(program->boldFont); colorAdjustment.setText("Color adjustment:"); saturation.name.setText("Saturation:"); @@ -24,7 +22,6 @@ VideoSettings::VideoSettings() { overscanVertical.name.setText("Vertical:"); overscanVertical.slider.setLength(17); - append(title, {~0, 0}, 5); append(colorAdjustment, {~0, 0}); append(saturation, {~0, 0}); append(gamma, {~0, 0}); diff --git a/target-ethos/settings/video.hpp b/target-ethos/settings/video.hpp index 8e4bf7b9..50c1914d 100644 --- a/target-ethos/settings/video.hpp +++ b/target-ethos/settings/video.hpp @@ -7,7 +7,6 @@ struct VideoSlider : HorizontalLayout { }; struct VideoSettings : SettingsLayout { - Label title; Label colorAdjustment; VideoSlider saturation; VideoSlider gamma; diff --git a/target-ethos/tools/cheat-database.cpp b/target-ethos/tools/cheat-database.cpp index d9bedb05..5063188a 100644 --- a/target-ethos/tools/cheat-database.cpp +++ b/target-ethos/tools/cheat-database.cpp @@ -50,7 +50,7 @@ void CheatDatabase::findCodes() { return; } - MessageWindow().setParent(*cheatEditor).setText("Sorry, no cheat codes were found.").information(); + MessageWindow().setParent(*tools).setText("Sorry, no cheat codes were found.").information(); } void CheatDatabase::addCodes() { diff --git a/target-ethos/tools/cheat-editor.cpp b/target-ethos/tools/cheat-editor.cpp index b477386b..010b8da8 100644 --- a/target-ethos/tools/cheat-editor.cpp +++ b/target-ethos/tools/cheat-editor.cpp @@ -1,15 +1,10 @@ CheatEditor* cheatEditor = nullptr; CheatEditor::CheatEditor() { - setGeometry({128, 128, 600, 360}); - windowManager->append(this, "CheatEditor"); - - setTitle("Cheat Editor"); - layout.setMargin(5); - cheatList.setHeaderText("Slot", "Code", "Description"); + cheatList.setHeaderText({"Slot", "Code", "Description"}); cheatList.setHeaderVisible(); cheatList.setCheckable(); - for(unsigned n = 0; n < Codes; n++) cheatList.append("", "", ""); + for(unsigned n = 0; n < Codes; n++) cheatList.append({"", "", ""}); codeLabel.setText("Code(s):"); descLabel.setText("Description:"); findButton.setText("Find Codes ..."); @@ -20,15 +15,14 @@ CheatEditor::CheatEditor() { Font::size(program->normalFont, "Description:").width ); - append(layout); - layout.append(cheatList, {~0, ~0}, 5); - layout.append(codeLayout, {~0, 0}, 5); + append(cheatList, {~0, ~0}, 5); + append(codeLayout, {~0, 0}, 5); codeLayout.append(codeLabel, {width, 0}, 5); codeLayout.append(codeEdit, {~0, 0}); - layout.append(descLayout, {~0, 0}, 5); + append(descLayout, {~0, 0}, 5); descLayout.append(descLabel, {width, 0}, 5); descLayout.append(descEdit, {~0, 0}); - layout.append(controlLayout, {~0, 0}); + append(controlLayout, {~0, 0}); controlLayout.append(findButton, {0, 0}, 5); controlLayout.append(spacer, {~0, 0}); controlLayout.append(resetButton, {80, 0}, 5); @@ -40,7 +34,7 @@ CheatEditor::CheatEditor() { descEdit.onChange = {&CheatEditor::updateDesc, this}; findButton.onActivate = {&CheatDatabase::findCodes, cheatDatabase}; resetButton.onActivate = [&] { - if(MessageWindow().setParent(*this).setText("All codes will be erased. Are you sure you want to do this?") + if(MessageWindow().setParent(*tools).setText("All codes will be erased. Are you sure you want to do this?") .question() == MessageWindow::Response::Yes) reset(); }; eraseButton.onActivate = {&CheatEditor::erase, this}; @@ -50,7 +44,7 @@ CheatEditor::CheatEditor() { } void CheatEditor::synchronize() { - layout.setEnabled(program->active); + setEnabled(program->active); if(cheatList.selected()) { unsigned n = cheatList.selection(); @@ -74,7 +68,7 @@ void CheatEditor::refresh() { string desc = cheat[n].code.empty() && cheat[n].desc.empty() ? "(empty)" : cheat[n].desc; lstring codes = code.split("+"); if(codes.size() > 1) code = {codes[0], "+..."}; - cheatList.modify(n, format<3>(1 + n), code, desc); + cheatList.setText(n, {format<3>(1 + n), code, desc}); } cheatList.autoSizeColumns(); } diff --git a/target-ethos/tools/cheat-editor.hpp b/target-ethos/tools/cheat-editor.hpp index b9548343..0a35317c 100644 --- a/target-ethos/tools/cheat-editor.hpp +++ b/target-ethos/tools/cheat-editor.hpp @@ -1,5 +1,4 @@ -struct CheatEditor : Window { - VerticalLayout layout; +struct CheatEditor : ToolsLayout { ListView cheatList; HorizontalLayout codeLayout; Label codeLabel; diff --git a/target-ethos/tools/state-manager.cpp b/target-ethos/tools/state-manager.cpp index 07c51b36..8ce38a85 100644 --- a/target-ethos/tools/state-manager.cpp +++ b/target-ethos/tools/state-manager.cpp @@ -1,14 +1,9 @@ StateManager* stateManager = nullptr; StateManager::StateManager() { - setGeometry({128, 128, 600, 360}); - windowManager->append(this, "StateManager"); - - setTitle("State Manager"); - layout.setMargin(5); - stateList.setHeaderText("Slot", "Description"); + stateList.setHeaderText({"Slot", "Description"}); stateList.setHeaderVisible(); - for(unsigned n = 0; n < Slots; n++) stateList.append(format<2>(1 + n), "(empty)"); + for(unsigned n = 0; n < Slots; n++) stateList.append({format<2>(1 + n), "(empty)"}); stateList.autoSizeColumns(); descLabel.setText("Description:"); saveButton.setText("Save"); @@ -16,12 +11,11 @@ StateManager::StateManager() { resetButton.setText("Reset"); eraseButton.setText("Erase"); - append(layout); - layout.append(stateList, {~0, ~0}, 5); - layout.append(descLayout, {~0, 0}, 5); + append(stateList, {~0, ~0}, 5); + append(descLayout, {~0, 0}, 5); descLayout.append(descLabel, {0, 0}, 5); descLayout.append(descEdit, {~0, 0}); - layout.append(controlLayout, {~0, 0}); + append(controlLayout, {~0, 0}); controlLayout.append(saveButton, {80, 0}, 5); controlLayout.append(loadButton, {80, 0}, 5); controlLayout.append(spacer, {~0, 0}); @@ -34,7 +28,7 @@ StateManager::StateManager() { saveButton.onActivate = {&StateManager::slotSave, this}; loadButton.onActivate = {&StateManager::slotLoad, this}; resetButton.onActivate = [&] { - if(MessageWindow().setParent(*this).setText("All states will be erased. Are you sure you want to do this?") + if(MessageWindow().setParent(*tools).setText("All states will be erased. Are you sure you want to do this?") .question() == MessageWindow::Response::Yes) reset(); }; eraseButton.onActivate = {&StateManager::slotErase, this}; @@ -44,7 +38,7 @@ StateManager::StateManager() { } void StateManager::synchronize() { - layout.setEnabled(program->active); + setEnabled(program->active); descEdit.setText(""); descEdit.setEnabled(false); @@ -59,7 +53,7 @@ void StateManager::synchronize() { void StateManager::refresh() { for(unsigned n = 0; n < Slots; n++) { - stateList.modify(n, format<2>(1 + n), slotLoadDescription(n)); + stateList.setText(n, {format<2>(1 + n), slotLoadDescription(n)}); } stateList.autoSizeColumns(); } diff --git a/target-ethos/tools/state-manager.hpp b/target-ethos/tools/state-manager.hpp index f5474ab5..daf06001 100644 --- a/target-ethos/tools/state-manager.hpp +++ b/target-ethos/tools/state-manager.hpp @@ -1,5 +1,4 @@ -struct StateManager : Window { - VerticalLayout layout; +struct StateManager : ToolsLayout { ListView stateList; HorizontalLayout descLayout; Label descLabel; diff --git a/target-ethos/tools/tools.cpp b/target-ethos/tools/tools.cpp index 0c6ddb9b..91afd7ad 100644 --- a/target-ethos/tools/tools.cpp +++ b/target-ethos/tools/tools.cpp @@ -2,3 +2,24 @@ #include "cheat-database.cpp" #include "cheat-editor.cpp" #include "state-manager.cpp" +Tools* tools = nullptr; + +ToolsLayout::ToolsLayout() { + setMargin(5); +} + +Tools::Tools() { + setGeometry({128, 128, 640, 360}); + windowManager->append(this, "Tools"); + + setTitle("Tools"); + + layout.setMargin(5); + panels.append("Cheat Editor", {resource::cheatEditor, sizeof resource::cheatEditor}); + panels.append("State Manager", {resource::stateManager, sizeof resource::stateManager}); + panels.setLayout(0, *cheatEditor); + panels.setLayout(1, *stateManager); + + append(layout); + layout.append(panels, {~0, ~0}); +} diff --git a/target-ethos/tools/tools.hpp b/target-ethos/tools/tools.hpp index d6df0ea7..06680369 100644 --- a/target-ethos/tools/tools.hpp +++ b/target-ethos/tools/tools.hpp @@ -1,3 +1,16 @@ +struct ToolsLayout : VerticalLayout { + ToolsLayout(); +}; + #include "cheat-database.hpp" #include "cheat-editor.hpp" #include "state-manager.hpp" + +struct Tools : Window { + VerticalLayout layout; + TabFrame panels; + + Tools(); +}; + +extern Tools* tools;