mirror of https://github.com/bsnes-emu/bsnes.git
213 lines
6.2 KiB
C++
213 lines
6.2 KiB
C++
|
namespace hiro {
|
||
|
|
||
|
static auto Canvas_drop(GtkWidget* widget, GdkDragContext* context, signed x, signed y,
|
||
|
GtkSelectionData* data, unsigned type, unsigned timestamp, pCanvas* p) -> void {
|
||
|
if(!p->state().droppable) return;
|
||
|
lstring paths = DropPaths(data);
|
||
|
if(paths.empty()) return;
|
||
|
p->self().doDrop(paths);
|
||
|
}
|
||
|
|
||
|
static auto Canvas_expose(GtkWidget* widget, GdkEventExpose* event, pCanvas* p) -> signed {
|
||
|
p->_onExpose(event);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static auto Canvas_mouseLeave(GtkWidget* widget, GdkEventButton* event, pCanvas* p) -> signed {
|
||
|
p->self().doMouseLeave();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static auto Canvas_mouseMove(GtkWidget* widget, GdkEventButton* event, pCanvas* p) -> signed {
|
||
|
p->self().doMouseMove({(signed)event->x, (signed)event->y});
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static auto Canvas_mousePress(GtkWidget* widget, GdkEventButton* event, pCanvas* p) -> signed {
|
||
|
switch(event->button) {
|
||
|
case 1: p->self().doMousePress(Mouse::Button::Left); break;
|
||
|
case 2: p->self().doMousePress(Mouse::Button::Middle); break;
|
||
|
case 3: p->self().doMousePress(Mouse::Button::Right); break;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static auto Canvas_mouseRelease(GtkWidget* widget, GdkEventButton* event, pCanvas* p) -> signed {
|
||
|
switch(event->button) {
|
||
|
case 1: p->self().doMouseRelease(Mouse::Button::Left); break;
|
||
|
case 2: p->self().doMouseRelease(Mouse::Button::Middle); break;
|
||
|
case 3: p->self().doMouseRelease(Mouse::Button::Right); break;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
auto pCanvas::construct() -> void {
|
||
|
gtkWidget = gtk_drawing_area_new();
|
||
|
gtk_widget_add_events(gtkWidget,
|
||
|
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK);
|
||
|
|
||
|
setDroppable(state().droppable);
|
||
|
_rasterize();
|
||
|
_redraw();
|
||
|
|
||
|
//todo: need to work around GTK+ library bug:
|
||
|
//after calling destruct(), construct() with state.droppable == true;
|
||
|
//GTK+ will throw SIGBUS inside g_signal_connect_data() on one of the below connections
|
||
|
|
||
|
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), "drag-data-received", G_CALLBACK(Canvas_drop), (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);
|
||
|
|
||
|
pWidget::construct();
|
||
|
}
|
||
|
|
||
|
auto pCanvas::destruct() -> void {
|
||
|
_release();
|
||
|
if(gtkWidget) gtk_widget_destroy(gtkWidget), gtkWidget = nullptr;
|
||
|
gtkParent = nullptr;
|
||
|
}
|
||
|
|
||
|
auto pCanvas::minimumSize() const -> Size {
|
||
|
return {max(0, state().size.width()), max(0, state().size.height())};
|
||
|
}
|
||
|
|
||
|
auto pCanvas::setColor(Color color) -> void {
|
||
|
mode = Mode::Color;
|
||
|
update();
|
||
|
}
|
||
|
|
||
|
auto pCanvas::setData(Size size) -> void {
|
||
|
mode = Mode::Data;
|
||
|
update();
|
||
|
}
|
||
|
|
||
|
auto pCanvas::setDroppable(bool droppable) -> void {
|
||
|
if(droppable) {
|
||
|
gtk_drag_dest_set(gtkWidget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
|
||
|
gtk_drag_dest_add_uri_targets(gtkWidget);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto pCanvas::setGeometry(Geometry geometry) -> void {
|
||
|
update();
|
||
|
pWidget::setGeometry(geometry);
|
||
|
}
|
||
|
|
||
|
auto pCanvas::setGradient(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight) -> void {
|
||
|
mode = Mode::Gradient;
|
||
|
update();
|
||
|
}
|
||
|
|
||
|
auto pCanvas::setIcon(const image& icon) -> void {
|
||
|
mode = Mode::Icon;
|
||
|
update();
|
||
|
}
|
||
|
|
||
|
auto pCanvas::update() -> void {
|
||
|
_rasterize();
|
||
|
_redraw();
|
||
|
}
|
||
|
|
||
|
auto pCanvas::_onExpose(GdkEventExpose* expose) -> void {
|
||
|
if(surface == nullptr) return;
|
||
|
|
||
|
signed sx = 0, sy = 0, dx = 0, dy = 0;
|
||
|
signed width = surfaceWidth;
|
||
|
signed height = surfaceHeight;
|
||
|
auto geometry = pSizable::state().geometry;
|
||
|
|
||
|
if(width <= geometry.width()) {
|
||
|
sx = 0;
|
||
|
dx = (geometry.width() - width) / 2;
|
||
|
} else {
|
||
|
sx = (width - geometry.width()) / 2;
|
||
|
dx = 0;
|
||
|
width = geometry.width();
|
||
|
}
|
||
|
|
||
|
if(height <= geometry.height()) {
|
||
|
sy = 0;
|
||
|
dy = (geometry.height() - height) / 2;
|
||
|
} else {
|
||
|
sy = (height - geometry.height()) / 2;
|
||
|
dy = 0;
|
||
|
height = geometry.height();
|
||
|
}
|
||
|
|
||
|
gdk_draw_pixbuf(gtk_widget_get_window(gtkWidget), nullptr, surface, sx, sy, dx, dy, width, height, GDK_RGB_DITHER_NONE, 0, 0);
|
||
|
}
|
||
|
|
||
|
auto pCanvas::_rasterize() -> void {
|
||
|
signed width = 0;
|
||
|
signed height = 0;
|
||
|
|
||
|
if(mode == Mode::Color || mode == Mode::Gradient) {
|
||
|
width = pSizable::state().geometry.width();
|
||
|
height = pSizable::state().geometry.height();
|
||
|
} else {
|
||
|
width = state().size.width();
|
||
|
height = state().size.height();
|
||
|
}
|
||
|
|
||
|
if(width <= 0 || height <= 0) return;
|
||
|
|
||
|
if(width != surfaceWidth || height != surfaceHeight) _release();
|
||
|
surfaceWidth = width;
|
||
|
surfaceHeight = height;
|
||
|
|
||
|
if(!surface) surface = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height);
|
||
|
uint32_t* buffer = (uint32_t*)gdk_pixbuf_get_pixels(surface);
|
||
|
|
||
|
if(mode == Mode::Color) {
|
||
|
uint32_t color = state().color.value();
|
||
|
for(auto n : range(width * height)) buffer[n] = color;
|
||
|
}
|
||
|
|
||
|
if(mode == Mode::Gradient) {
|
||
|
image fill;
|
||
|
fill.allocate(width, height);
|
||
|
fill.gradient(
|
||
|
state().gradient[0].value(), state().gradient[1].value(), state().gradient[2].value(), state().gradient[3].value()
|
||
|
);
|
||
|
memory::copy(buffer, fill.data, fill.size);
|
||
|
}
|
||
|
|
||
|
if(mode == Mode::Icon) {
|
||
|
auto icon = state().icon;
|
||
|
icon.scale(width, height);
|
||
|
icon.transform(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0);
|
||
|
memory::copy(buffer, icon.data, icon.size);
|
||
|
}
|
||
|
|
||
|
if(mode == Mode::Data) {
|
||
|
memory::copy(buffer, state().data.data(), state().data.size() * sizeof(uint32_t));
|
||
|
}
|
||
|
|
||
|
//ARGB -> ABGR conversion
|
||
|
for(auto n : range(width * height)) {
|
||
|
uint32_t color = *buffer;
|
||
|
color = (color & 0xff00ff00) | ((color & 0xff0000) >> 16) | ((color & 0x0000ff) << 16);
|
||
|
*buffer++ = color;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto pCanvas::_redraw() -> void {
|
||
|
if(gtk_widget_get_realized(gtkWidget)) {
|
||
|
gdk_window_invalidate_rect(gtk_widget_get_window(gtkWidget), nullptr, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto pCanvas::_release() -> void {
|
||
|
if(surface) {
|
||
|
g_object_unref(surface);
|
||
|
surface = nullptr;
|
||
|
}
|
||
|
surfaceWidth = 0;
|
||
|
surfaceHeight = 0;
|
||
|
}
|
||
|
|
||
|
}
|