Image support
This commit is contained in:
@@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
|
|
||||||
add_library(quip SHARED src/quip.cpp src/quip.h)
|
add_library(quip SHARED src/quip.cpp src/quip.h)
|
||||||
|
|
||||||
target_link_libraries(quip PUBLIC SDL3 SDL3_ttf)
|
target_link_libraries(quip PUBLIC SDL3 SDL3_ttf SDL3_image)
|
||||||
|
|
||||||
target_include_directories(quip PUBLIC
|
target_include_directories(quip PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
|
||||||
|
|||||||
BIN
example/dingus.png
Normal file
BIN
example/dingus.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 514 KiB |
@@ -1,7 +1,33 @@
|
|||||||
#include <quip.h>
|
#include <quip.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
Quip::Window window("Quip Window", 800, 600);
|
bool goingDown = true, goingRight = true;
|
||||||
|
Quip::Window window("Quip Window", 800, 600, [&window, &goingDown, &goingRight]() {
|
||||||
|
auto* imagewidget = window.getChild("image");
|
||||||
|
auto& image = std::get<Quip::Widget::Image>(*imagewidget);
|
||||||
|
if (goingDown) {
|
||||||
|
image.ypos ++;
|
||||||
|
if (image.ypos + image.height > window.height) {
|
||||||
|
goingDown = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
image.ypos --;
|
||||||
|
if (image.ypos < 0) {
|
||||||
|
goingDown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (goingRight) {
|
||||||
|
image.xpos ++;
|
||||||
|
if (image.xpos + image.width > window.width) {
|
||||||
|
goingRight = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
image.xpos --;
|
||||||
|
if (image.xpos < 0) {
|
||||||
|
goingRight = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
int buttonPresses = 0;
|
int buttonPresses = 0;
|
||||||
window.addChild("textLabel", Quip::Widget::TextLabel("Hi there!", 10, 10));
|
window.addChild("textLabel", Quip::Widget::TextLabel("Hi there!", 10, 10));
|
||||||
window.addChild("button", Quip::Widget::Button("Click me!", [&window, &buttonPresses]() {
|
window.addChild("button", Quip::Widget::Button("Click me!", [&window, &buttonPresses]() {
|
||||||
@@ -11,5 +37,6 @@ int main() {
|
|||||||
std::get<Quip::Widget::TextLabel>(*widget).text = "You have clicked the button " + std::to_string(buttonPresses) + " times!";
|
std::get<Quip::Widget::TextLabel>(*widget).text = "You have clicked the button " + std::to_string(buttonPresses) + " times!";
|
||||||
}
|
}
|
||||||
}, 10, 50));
|
}, 10, 50));
|
||||||
|
window.addChild("image", Quip::Widget::Image("dingus.png", 300, 300, 100, 100));
|
||||||
return window.run();
|
return window.run();
|
||||||
}
|
}
|
||||||
|
|||||||
39
src/quip.cpp
39
src/quip.cpp
@@ -1,5 +1,6 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3_ttf/SDL_ttf.h>
|
#include <SDL3_ttf/SDL_ttf.h>
|
||||||
|
#include <SDL3_image/SDL_image.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
@@ -21,7 +22,27 @@ namespace Quip {
|
|||||||
SDL_DestroySurface(surface);
|
SDL_DestroySurface(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
Button::Button(std::string text, std::function<void()> callback, int xpos, int ypos) : text(std::move(text)), callback(std::move(callback)), xpos(xpos), ypos(ypos) {}
|
Image::Image(const std::string& filepath, int xpos, int ypos, int width, int height) : width(width), height(height), xpos(xpos), ypos(ypos) {
|
||||||
|
surface = IMG_Load(filepath.c_str());
|
||||||
|
if (surface == nullptr) {
|
||||||
|
Window().error(4, "Couldn't load image " + filepath);
|
||||||
|
}
|
||||||
|
if (width < 0) {
|
||||||
|
this->width = surface->w;
|
||||||
|
}
|
||||||
|
if (height < 0) {
|
||||||
|
this->height = surface->h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::render(const Window &window) const {
|
||||||
|
SDL_Texture* texture = SDL_CreateTextureFromSurface(window.renderer, surface);
|
||||||
|
SDL_FRect destRect = {static_cast<float>(xpos), static_cast<float>(ypos), static_cast<float>(width), static_cast<float>(height)};
|
||||||
|
SDL_RenderTexture(window.renderer, texture, nullptr, &destRect);
|
||||||
|
SDL_DestroyTexture(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
Button::Button(std::string text, std::function<void()> callback, int xpos, int ypos) : text(std::move(text)), onCLick(std::move(callback)), xpos(xpos), ypos(ypos) {}
|
||||||
void Button::render(const Window &window) const {
|
void Button::render(const Window &window) const {
|
||||||
SDL_SetRenderDrawColor(window.renderer, 40, 60, 80, 255); // Dark gray background
|
SDL_SetRenderDrawColor(window.renderer, 40, 60, 80, 255); // Dark gray background
|
||||||
SDL_FRect backgroundRect = {static_cast<float>(xpos), static_cast<float>(ypos), static_cast<float>(width), static_cast<float>(height)};
|
SDL_FRect backgroundRect = {static_cast<float>(xpos), static_cast<float>(ypos), static_cast<float>(width), static_cast<float>(height)};
|
||||||
@@ -38,7 +59,7 @@ namespace Quip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Window::Window() = default;
|
Window::Window() = default;
|
||||||
Window::Window(std::string title, int width, int height) : title(std::move(title)), width(width), height(height) {
|
Window::Window(std::string title, int width, int height, const std::function<void()>& onFrame) : title(std::move(title)), width(width), height(height), onFrame(onFrame) {
|
||||||
windowCount ++;
|
windowCount ++;
|
||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
error(2, "width is " + std::to_string(width) + " and height is " + std::to_string(height));
|
error(2, "width is " + std::to_string(width) + " and height is " + std::to_string(height));
|
||||||
@@ -60,7 +81,7 @@ namespace Quip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::error(int errCode, const std::string& context) {
|
void Window::error(int errCode, const std::string& context) const {
|
||||||
SDL_DestroyWindow(window);
|
SDL_DestroyWindow(window);
|
||||||
SDL_DestroyRenderer(renderer);
|
SDL_DestroyRenderer(renderer);
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
@@ -79,6 +100,9 @@ namespace Quip {
|
|||||||
case 3:
|
case 3:
|
||||||
std::cerr << "Widget name error" << std::endl;
|
std::cerr << "Widget name error" << std::endl;
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
std::cerr << "File error" << std::endl;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (!context.empty()) {
|
if (!context.empty()) {
|
||||||
std::cerr << "Context: " << context << std::endl;
|
std::cerr << "Context: " << context << std::endl;
|
||||||
@@ -97,6 +121,8 @@ namespace Quip {
|
|||||||
case 3:
|
case 3:
|
||||||
throw std::runtime_error("Quip::Window() Widget name error");
|
throw std::runtime_error("Quip::Window() Widget name error");
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
throw std::runtime_error("Quip::Window() File error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +143,7 @@ namespace Quip {
|
|||||||
|
|
||||||
int Window::run() {
|
int Window::run() {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
onFrame();
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
@@ -133,8 +160,8 @@ namespace Quip {
|
|||||||
// Check if click is inside button bounds
|
// Check if click is inside button bounds
|
||||||
if (mouseX >= button.xpos && mouseX <= button.xpos + button.width &&
|
if (mouseX >= button.xpos && mouseX <= button.xpos + button.width &&
|
||||||
mouseY >= button.ypos && mouseY <= button.ypos + button.height) {
|
mouseY >= button.ypos && mouseY <= button.ypos + button.height) {
|
||||||
if (button.callback) {
|
if (button.onCLick) {
|
||||||
button.callback();
|
button.onCLick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,6 +176,8 @@ namespace Quip {
|
|||||||
std::get<Widget::TextLabel>(widget).render(*this);
|
std::get<Widget::TextLabel>(widget).render(*this);
|
||||||
} else if (std::holds_alternative<Widget::Button>(widget)) {
|
} else if (std::holds_alternative<Widget::Button>(widget)) {
|
||||||
std::get<Widget::Button>(widget).render(*this);
|
std::get<Widget::Button>(widget).render(*this);
|
||||||
|
} else if (std::holds_alternative<Widget::Image>(widget)) {
|
||||||
|
std::get<Widget::Image>(widget).render(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!SDL_RenderPresent(renderer)) {
|
if (!SDL_RenderPresent(renderer)) {
|
||||||
|
|||||||
18
src/quip.h
18
src/quip.h
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3_ttf/SDL_ttf.h>
|
#include <SDL3_ttf/SDL_ttf.h>
|
||||||
|
#include <SDL3_image/SDL_image.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -19,17 +20,25 @@ namespace Quip {
|
|||||||
explicit TextLabel(std::string text, int xpos, int ypos);
|
explicit TextLabel(std::string text, int xpos, int ypos);
|
||||||
TextLabel();
|
TextLabel();
|
||||||
};
|
};
|
||||||
|
class Image {
|
||||||
|
SDL_Surface* surface;
|
||||||
|
public:
|
||||||
|
int width = 0, height = 0;
|
||||||
|
int xpos = 0, ypos = 0;
|
||||||
|
void render(const Window &window) const;
|
||||||
|
Image(const std::string& filepath, int xpos, int ypos, int width = -1, int height = -1);
|
||||||
|
};
|
||||||
class Button {
|
class Button {
|
||||||
public:
|
public:
|
||||||
std::string text;
|
std::string text;
|
||||||
std::function<void()> callback;
|
std::function<void()> onCLick;
|
||||||
int xpos = 0, ypos = 0;
|
int xpos = 0, ypos = 0;
|
||||||
int width = 100, height = 30;
|
int width = 100, height = 30;
|
||||||
void render(const Window &window) const;
|
void render(const Window &window) const;
|
||||||
explicit Button(std::string text, std::function<void()> callback, int xpos, int ypos);
|
explicit Button(std::string text, std::function<void()> callback, int xpos, int ypos);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::variant<TextLabel, Button> Widget;
|
typedef std::variant<TextLabel, Button, Image> Widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Window {
|
class Window {
|
||||||
@@ -41,11 +50,12 @@ namespace Quip {
|
|||||||
std::string title;
|
std::string title;
|
||||||
int width, height;
|
int width, height;
|
||||||
std::map<std::string, Widget::Widget> widgets;
|
std::map<std::string, Widget::Widget> widgets;
|
||||||
void error(int errCode = 0, const std::string& context = "");
|
std::function<void()> onFrame;
|
||||||
|
void error(int errCode = 0, const std::string& context = "") const;
|
||||||
int run();
|
int run();
|
||||||
void addChild(const std::string& id, const Widget::Widget& widget);
|
void addChild(const std::string& id, const Widget::Widget& widget);
|
||||||
Widget::Widget* getChild(const std::string& id);
|
Widget::Widget* getChild(const std::string& id);
|
||||||
explicit Window(std::string title, int width = 800, int height = 600);
|
explicit Window(std::string title, int width = 800, int height = 600, const std::function<void()>& onFrame = nullptr);
|
||||||
Window();
|
Window();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user