Sign up

Structure of a program

Hello, sorry for the broad question but I'm a noob in D, gtk and gtkD... I'll try to narrow the question as much as I can.

I'm using glade to design the interface of my program, and glib-compile-resources to generate a .c file that I link to my program. So at some point I have:

Builder builder = new Builder();
if (!builder.addFromResource("/org/gtk/testprogram/main.glade")) {
    throw new GuiInitException("Error obtaining frmMain window");
}
...
builder.connectSignals(myData);

and then I have a bunch of extern(C) functions that are handling the signals.

Let's talk about myData now: at some point I realized some widgets needed to set the text of other widgets in response to the user's input, so I had to pass them some pointer. My first attempt was to set myData = cast(void*)builder. That worked, but I also needed other stuff initialized in main, and that I would normally pass to a constructor, ie:

void main() {
    auto config = new Config("/path/to/config");
    auto myWin = new MyWindow(config);
}

so I made a structure that paired together the builder and config. Good, it works, but what's the deal with all those cast!Label(cast!GlobalStruct*(myData).getObject("someLabel")) and asserting !is null...

So my last attempt was to make a class like this:

class MyWindow : ApplicationWindow {
    this(Config conf) {
        ...
        Builder builder = new Builder();
        builder.addFromResource("/org/gtk/testprogram/main.glade");
        m_label = cast!Label(builder.getObject("some_label");
        ...
        //and finally:
        super(cast!ApplicationWindow(builder.getObject("myWindow")));
    }

with the intention of setting myData to an instance of this class, so that all the extern(C) events become methods (void* data representing "this").
I wish that worked... ApplicationWindow has no copy constructor, and getStruct() is protected so I can't get it from the object returned by the builder.

So my questions are:

  • did I take any "right" approach at any point during my design?
  • How is one supposed to inherit from classes the whose objects are automatically created by Builder?
  • Is there a way to differentiate the data being passed to events, so that I can pass the correct "this" pointer even if I have more than one window in my program? Or am I obliged to pack all of my "this" pointers into a global structure that is passed to every signal handler and trust them to access the right one? (sometimes it might be tempting to take shortcuts...)

Re: Structure of a program

Can't find the edit button... just for the sake of completeness, I solved the constructor problem by adding:

class MyWindow {
    ApplicationWindow myWin;
    alias myWin this;
};

(something equivalent to this, myWin is private and I have a public accessor, which is aliased to this).
Somehow, this doesn't feel right tho.

Re: Structure of a program

On 01/22/2015 12:43 AM, King_DuckZ wrote:

So my questions are:

  • did I take any "right" approach at any point during my design?

Using builder.connectSignals limmits the amount of information you can
pass to the callbacks. To global data and the information passed as the
userData in the connectSignals call.

  • How is one supposed to inherit from classes the whose objects are automatically created by Builder?

I don't think you can, GtkBuilder wouldn't know how to instantiate the
derived class.

  • Is there a way to differentiate the data being passed to events, so that I can pass the correct "this" pointer even if I have more than one window in my program? Or am I obliged to pack all of my "this" pointers into a global structure that is passed to every signal handler and trust them to access the right one? (sometimes it might be tempting to take shortcuts...)

Take a look at the Builder example[0] it connects the signals manually,
that way the callbacks are proper methods/delegates.

[0]
https://github.com/gtkd-developers/GtkD/blob/master/demos/builder/builderTest.d

Re: Structure of a program

On Thu, 22 Jan 2015 22:35:28 +0100, Mike Wey wrote:

  • How is one supposed to inherit from classes the whose objects are automatically created by Builder?

I don't think you can, GtkBuilder wouldn't know how to instantiate the
derived class.

What about my MyWindow example? That way I can customize the window class, if only I could copy-construct its base class from the ApplicationWindow returned by the builder. Any way I can do that?

Re: Structure of a program

Never mind, I'm having too many troubles using D (gtkD being only one of them), so I'll be converting my project to c++ before I get too far in the development.

I'd just like to share the cmake file I wrote to compile gtkD. I can't say it's perfect but it works for me, I hope it will be useful for somebody else as well.

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../../cmake/Modules/cmake-d" "${CMAKE_MODULE_PATH}")
project (GtkD C D)

## sanity check: sometimes CMAKE_SIZEOF_VOID_P just vanishes (when updating CMake).
if (NOT CMAKE_SIZEOF_VOID_P)
    message(FATAL_ERROR "'CMAKE_SIZEOF_VOID_P' is undefined. Thus you should delete CMakeFiles (the directory) and the CMakeCache.txt and rerun CMake again! This is some weird CMake bug that seems to appear when updating the CMake version.")
else()
    math(EXPR BITS "8*${CMAKE_SIZEOF_VOID_P}")
endif()

set(CMAKE_D_FLAGS_DEBUG "${CMAKE_D_FLAGS_DEBUG}")
set(CMAKE_D_FLAGS_RELEASE "${CMAKE_D_FLAGS_RELEASE}")

if("${CMAKE_D_COMPILER_ID}" STREQUAL "gdc")
    set(CMAKE_D_FLAGS_DEBUG "${CMAKE_D_FLAGS_DEBUG} -O0 -g")
    set(CMAKE_D_FLAGS_RELEASE "${CMAKE_D_FLAGS_RELEASE} -O2")
else()
    set(CMAKE_D_FLAGS_RELEASE "${CMAKE_D_FLAGS_RELEASE} -O")
endif()

if ("${CMAKE_D_COMPILER_ID}" STREQUAL "LDC")
    add_definitions(-relocation-model=pic)
else()
    add_definitions(-fPIC)
endif()

set(MAJOR "2")
set(MINOR "4")
set(BUGFIX "1")
set(GTKD_VERSION "${MAJOR}.${MINOR}.${BUGFIX}")
set(SO_VERSION "0")

# From http://francesco-cek.com/cmake-and-gtk-3-the-easy-way/
# Use the package PkgConfig to detect GTK+ headers/library files
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
# Setup CMake to use GTK+, tell the compiler where to look for headers
# and to the linker where to look for libraries
link_directories(${GTK3_LIBRARY_DIRS})
# Add other flags to the compiler
if ("${CMAKE_D_COMPILER_ID}" STREQUAL "gdc")
    add_definitions(${GTK3_CFLAGS_OTHER})
endif()

include_directories(
    GtkD/src
)

set(LIBNAME_GTKD "gtkd-${MAJOR}")
file(GLOB_RECURSE SOURCES_GTKD "GtkD/src/*.d")
set(LIBNAME_GTKDGL "gtkdgl-${MAJOR}")
file(GLOB_RECURSE SOURCES_GTKDGL "GtkD/srcgl/*.d")
set(LIBNAME_GTKDSV "gtkdsv-${MAJOR}")
file(GLOB_RECURSE SOURCES_GTKDSV "GtkD/srcsv/*.d")
set(LIBNAME_GTKDGDA "gtkdgda-${MAJOR}")
file(GLOB_RECURSE SOURCES_GTKDGDA "GtkD/srcgda/*.d")
set(LIBNAME_GSTREAMERD "gstreamerd-${MAJOR}")
file(GLOB_RECURSE SOURCES_GSTREAMERD "GtkD/srcgstreamer/*.d")
set(LIBNAME_VTED "vted-${MAJOR}")
file(GLOB_RECURSE SOURCES_VTED "GtkD/srcvte/*.d")

add_library(${LIBNAME_GTKD} ${SOURCES_GTKD})
add_library(${LIBNAME_GTKDGL} ${SOURCES_GTKDGL})
add_library(${LIBNAME_GTKDSV} ${SOURCES_GTKDSV})
add_library(${LIBNAME_GTKDGDA} ${SOURCES_GTKDGDA})
add_library(${LIBNAME_GSTREAMERD} ${SOURCES_GSTREAMERD})
add_library(${LIBNAME_VTED} ${SOURCES_VTED})

SET_TARGET_PROPERTIES(${LIBNAME_GTKD} PROPERTIES LINK_FLAGS -m${BITS})
SET_TARGET_PROPERTIES(${LIBNAME_GTKDGL} PROPERTIES LINK_FLAGS -m${BITS})
SET_TARGET_PROPERTIES(${LIBNAME_GTKDSV} PROPERTIES LINK_FLAGS -m${BITS})
SET_TARGET_PROPERTIES(${LIBNAME_GTKDGDA} PROPERTIES LINK_FLAGS -m${BITS})
SET_TARGET_PROPERTIES(${LIBNAME_GSTREAMERD} PROPERTIES LINK_FLAGS -m${BITS})
SET_TARGET_PROPERTIES(${LIBNAME_VTED} PROPERTIES LINK_FLAGS -m${BITS})
add_definitions(-m${BITS})

SET_TARGET_PROPERTIES(${LIBNAME_GTKDGL} PROPERTIES COMPILE_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}/GtkD/srcgl")
SET_TARGET_PROPERTIES(${LIBNAME_GTKDSV} PROPERTIES COMPILE_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}/GtkD/srcsv")
SET_TARGET_PROPERTIES(${LIBNAME_GTKDGDA} PROPERTIES COMPILE_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}/GtkD/srcgda")
SET_TARGET_PROPERTIES(${LIBNAME_GSTREAMERD} PROPERTIES COMPILE_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}/GtkD/srcgstreamer")
SET_TARGET_PROPERTIES(${LIBNAME_VTED} PROPERTIES COMPILE_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}/GtkD/srcvte")

target_link_libraries(${LIBNAME_GTKD} dl ${GTK3_LIBRARIES})
target_link_libraries(${LIBNAME_GTKDGL} dl ${GTK3_LIBRARIES})
target_link_libraries(${LIBNAME_GTKDSV} dl ${GTK3_LIBRARIES})
target_link_libraries(${LIBNAME_GTKDGDA} dl ${GTK3_LIBRARIES})
target_link_libraries(${LIBNAME_GSTREAMERD} dl ${GTK3_LIBRARIES})
target_link_libraries(${LIBNAME_VTED} dl ${GTK3_LIBRARIES})

Note that the "C" in the project statement is required to work around a bug in cmake-d. Without it, CMAKESIZEOFVOID_P would be undefined.
You might need to tweak some paths, some version numbers, but in general it should do its job.

Re: Structure of a program

On Fri, 23 Jan 2015 10:27:04 GMT, King_DuckZ wrote:

On Thu, 22 Jan 2015 22:35:28 +0100, Mike Wey wrote:

  • How is one supposed to inherit from classes the whose objects are automatically created by Builder?

I don't think you can, GtkBuilder wouldn't know how to instantiate the
derived class.

What about my MyWindow example? That way I can customize the window class, if only I could copy-construct its base class from the ApplicationWindow returned by the builder. Any way I can do that?

I've always solved this situation with 'reparenting':

class MainWindow : Window, View {
...
private void prepare_ui () {

// The very first widget inside the window, this box holds all the
// other widgets
Box b1 = cast(Box) mbuilder.getObject("box1");

// Now one by one get the other widgets inside box1...

// FileChooser buttons
mimagechooser = cast(FileChooserButton) mbuilder.getObject("imagefilechooserbutton");
init_imagefilechooser_button ();

mxmlchooser = cast(FileChooserButton) mbuilder.getObject("xmlfilechooserbutton");
init_xmlfilechooser_button ();

// Drawing Area
mpage_image = cast(DrawingArea) mbuilder.getObject("page_image");
if (mpage_image !is null) {
  mpage_image.addOnDraw (&redraw_page);
  mpage_image.addOnButtonPress (&button_press);
  mpage_image.setHasTooltip (true);
  mpage_image.addOnQueryTooltip (&query_tooltip);
}
...
////////////////////////////////////////////////////////
// Finally reparent the glade window into this one... //
////////////////////////////////////////////////////////

//alias this Widget;
//alias Widget = this;
b1.reparent ( this );

}