Jump to content

Ultra AppKit - Is it possible to get a DLL version of Ultra AppKit?


buzzdx
 Share

Recommended Posts

I bought Ultra AppKit today and tried adding it to one my of my existing projects. When trying to compile I got an error about mismatching types of Multithreaded DLL (MD) and Multithreaded (MT) runtimes. I tried creating a fresh project and setting it to /MT which resulted in the code getting compiled and running fine.

Since I need to also use other libraries, mostly the Poco C++ libraries, is it possible to add an AppKit.dll and accompanying AppKit.lib using Multithreaded DLL to the release version of AppKit? Or am I missing something obvious here to get it working like it is?

On a side note: I really like the API and will use it to teach my friend C++ GUI applications. Yet there are some things missing imo, like lists with multiple fields and the ability to use icons in lists and trees. I will try to implement these in a custom widget - could you please add the source code for the tree widget to the github page? Also the Ultra Engine app that comes with steam does not seem to work for me. I enter user/password, but i cannot click the button to login. Any idea why that is?

Many thanks in advance,

Link to comment
Share on other sites

I don't think a DLL will support a C++ API, and even if it did there is still usually a static library the program must link to.

Since POCO seems to be open-source, why can't it be compiled with MT/MTd code generation?

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

"I don't think a DLL will support a C++ API"  --  I'm not sure I understand this part. Does it make a difference if the code is delivered in a DLL or as a static library?

"and even if it did there is still usually a static library the program must link to."  --  That's right, Poco comes with the DLLs and then there's static libs the linker needs.

"Since POCO seems to be open-source, why can't it be compiled with MT/MTd code generation? "  --  That is probably possible to achieve. However, since everything else I encountered so far did work with the /MD setting, I thought it would be easier to change the one that's different rather than all the others, and therefore went on to ask ?

Thinking of wxwidgets right now it comes to my mind that you don't even need a DLL to use the /MD flags. Wxwidgets compiles to static libraries which work with the /MD setting.

So maybe I should rephrase the question to:   Is it possible to get the AppKit.lib compiled with /MD flag instead of /MT?

Thanks for your time and answers so far.

Link to comment
Share on other sites

DLLs expose a C API other programs can hook into. I don't think there is any way to expose a C++ API with classes and methods, in a DLL, or in a cross-platform manner (.so, .dylib, .dll).

I really don't want to add another compile step to my build process. It's tough enough right now to keep track of eight different builds in three different installers.

Then there are all the static libraries my software builds in a separate step, which I am assuming are all using MT / MTd mode...

Does anyone even know what these do? I don't see any difference in the file size if I compile a simple VS project with either mode.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

I think you can expose classes and methods via a DLL. Poco for example does it. But, at least for code compiled with Visual Studio, it comes with a drawback: The exported classes can only be linked with other modules that use the same specific compiler version and compiler settings. For Poco it works by downloading the source, building the dlls and then using them with exactly the same compiler and settings. So in case of Ultra AppKit this might not be a good choice as the consumers of the dll would need to use said settings and the same version of Visual Studio.

I understand that it's already a lot to keep track of. I didn't think of that before, and assumed it would not be a great deal.

The difference between MD and MT is the runtime they use. MT uses a static runtime version, MD links dynamically to the msvcrXYZ.dll, xyz being the version number. Info taken from here: https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=msvc-160

So my guess is that all the other libs you use also are set to /MT now, else they would probably not work. Which means in case you would consider changing to /MD, you would indeed have to change the whole chain. Would it be possible to check one of the lib's project settings to make sure if you have time for that?

I will try and compile Poco as static libs and see how that goes for now. I'll post an update later.

 

  • Thanks 1
Link to comment
Share on other sites

Hi again,

so I did compile Poco as static MT libs and so far it seems to work fine ?

Had to fiddle a bit with the project settings to get rid of the console window. Your template was helpful for that.

One thing I could not find in the documentation was how to set a window icon. Is there a function for it, or do I need to invoke win32 for that?

After that my appkit adventures can begin ^^

 

Link to comment
Share on other sites

For the icon, somebody did post somewhere here how to do it the "right way" which is how I did it for my game. However the method is Windows exclusive.

I can reshare it via a blog post when I'm infront of my code base again.

Cyclone - Ultra Game System - Component PreprocessorTex2TGA - Darkness Awaits Template (Leadwerks)

If you like my work, consider supporting me on Patreon!

Link to comment
Share on other sites

Turns out it's really easy to set a window icon. Include the icon in your .rc file like this:

 

appicon		ICON	"path_to_icon/icon.ico"

 

And then in C++ set the icon like this:
 

//Create window
auto mainwindow = CreateWindow("Ultra App Kit", 0, 0, 1024, 768, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR | WINDOW_RESIZABLE);
  
HICON hIcon = LoadIconA(GetModuleHandle(0), "APPICON");
SendMessage(mainwindow->GetHandle(), WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
SendMessage(mainwindow->GetHandle(), WM_SETICON, ICON_BIG, (LPARAM)hIcon);

 

  • Thanks 1
  • Upvote 1
Link to comment
Share on other sites

I'm trying to change the background color of a tree view. It doesn't seem to react to it, neither using

tree->SetColor(0, 0, 255, 1.0f, WIDGETCOLOR_BACKGROUND);

nor by creating a custom theme and changing bg color there. Am I doing it wrong or does it not support a custom background color?

Changing e.g. the border does work.

Also could you please upload the treeview's widget code to github? With that I could change the bg color myself by making it a custom control.

Link to comment
Share on other sites

Thanks again, that works. Just for your information: I'm trying to find the information I'm asking for in the documentation first before coming here. It's just that I cannot find certain things or they don't work like I expected. Please bear with me ?

Been trying for an hour now to get the treeview to select a node on program startup. I managed to emit this event

EmitEvent(EVENT_WIDGETSELECT, tree, 1, 0, 0, 0, 0, rootnode);

and catch it with my event listener. I can print the node name and it is the right node (rootnode) that gets selected. However, the treeview does not show visually that it is selected. Also, when I click on the rootnode with the mouse (after it has been selected by the emitted event) it gets selected again (my event listener runs again). After it got selected manually with the mouse, and visually shows that this node is selected., the event listener will not run again when clicking on the same node again with the mouse.

Can this be solved programatically?

 

Link to comment
Share on other sites

This example will do it. Note I had to add a call to Widget::Draw() right before the first call to SelectNode() because I just discovered a bug that this prevents from getting triggered:

#include "UltraEngine.h"

using namespace UltraEngine;

int main(int argc, const char* argv[])
{
    //Get the displays
    auto displays = GetDisplays();

    //Create a window
    auto window = CreateWindow("Ultra Engine", 0, 0, 800, 600, displays[0], WINDOW_TITLEBAR | WINDOW_RESIZABLE);

    //Create User Interface
    auto ui = CreateInterface(window);

    //Create widget
    auto sz = ui->root->ClientSize();
    auto treeview = CreateTreeView(10, 10, sz.x - 20, sz.y - 20, ui->root, TREEVIEW_DRAGANDDROP | TREEVIEW_DRAGINSERT);
    treeview->SetLayout(1, 1, 1, 1);

    auto node = treeview->root->AddNode("Node 1");
    node->AddNode("Subnode 1");
    node->AddNode("Subnode 2");
    node->AddNode("Subnode 3");

    treeview->Draw(0, 0, 0, 0);
    treeview->SelectNode(node);

    node = treeview->root->AddNode("Node 2");
    node->AddNode("Subnode 1");
    node->AddNode("Subnode 2");
    node->AddNode("Subnode 3");

    node = treeview->root->AddNode("Node 3");
    node->AddNode("Subnode 1");
    node->AddNode("Subnode 2");
    node->AddNode("Subnode 3");

    while (true)
    {
        const auto& event = WaitEvent();
        switch (event.id)
        {
        case EVENT_WIDGETSELECT:
            if (event.source == treeview and event.data == 1)
            {
                node = event.extra->As<Widget>();
                Print("Selected: " + node->text);
            }
            break;
        case EVENT_WIDGETACTION:
            if (event.source == treeview)
            {
                node = event.extra->As<Widget>();
                Print("Action: " + node->text);
                if (!node->kids.empty())
                {
                    if (node->Collapsed())
                    {
                        node->Expand();
                    }
                    else
                    {
                        node->Collapse();
                    }
                }
            }
            break;
        case EVENT_WIDGETDROP:
        {
            auto child = event.source->As<Widget>();
            auto parent = event.extra->As<Widget>();
            child->SetParent(parent, event.data);
        }
        break;
        case EVENT_WINDOWCLOSE:
            return 0;
            break;
        }
    }
    return 0;
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

2 minutes ago, buzzdx said:

I feel stupid now for not having found SelectNode ^^ Well, at least it helped finding that bug I guess.

Not your fault! It was missing in the documentation and I just added it in quickly. I don't know why it was not there but it should be.

  • Haha 1

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

So, I moved forward with my project, which is a backup tool in which you can setup all the files and directories you want to backup, and then do the actual backup which automatically zips the files and folders. I've come across another question though: Is there a method to delete nodes from a tree?

I tried ClearItems but that does not seem to have an effect on treeviews.

I found that the nodes are vectors of widgets. Trying to clear the kids vector (no matter if for tree->root or an actual node) results in a debug break / call to abort() - but i can't really tell what's causing it.

What I need is to delete a node that has been selected.

Another question I'm having right now is how to force redraw the tree. I have a textfield in which the user can change the name of nodes. When enter is pressed the node's text is changed. But it doesn't show in the tree. I tried:

tree->Invalidate(true);
tree->Refresh();
tree->Redraw();
tree->Draw(0,0,tree->GetSize().x, tree->GetSize().y);

as well as calling all four on a node rather than the tree.

But none of them had an effect. It only shows the change when selecting another node. What I found as a workaround to both of these problems is to recreate the tree completely, that is doing another CreateTreeView on the same variable. This should remove the old one from memory as it goes out of scope. But it's not a clean solution. What is the right way to update nodes and directly show it, and how to delete nodes from a treeview?

Link to comment
Share on other sites

You are right, setting a node text does not trigger a redraw of the treeview. Thank you for pointing this out!

Until the next build fixes this, here is a quick way to force a redraw:

#include "UltraEngine.h"

using namespace UltraEngine;

int main(int argc, const char* argv[])
{
    //Get the displays
    auto displays = GetDisplays();

    //Create a window
    auto window = CreateWindow("Ultra Engine", 0, 0, 800, 600, displays[0], WINDOW_TITLEBAR | WINDOW_RESIZABLE);

    //Create User Interface
    auto ui = CreateInterface(window);

    //Create widget
    auto sz = ui->root->ClientSize();
    auto treeview = CreateTreeView(10, 10, sz.x - 20, sz.y - 20, ui->root, TREEVIEW_DRAGANDDROP | TREEVIEW_DRAGINSERT);
    treeview->SetLayout(1, 1, 1, 1);

    auto node = treeview->root->AddNode("Node 1");
    node->AddNode("Subnode 1");
    node->AddNode("Subnode 2");
    node->AddNode("Subnode 3");

    node = treeview->root->AddNode("Node 2");
    node->AddNode("Subnode 1");
    node->AddNode("Subnode 2");
    node->AddNode("Subnode 3");

    node = treeview->root->AddNode("Node 3");
    node->AddNode("Subnode 1");
    node->AddNode("Subnode 2");
    node->AddNode("Subnode 3");

    while (true)
    {
        const auto& event = WaitEvent();
        switch (event.id)
        {
        case EVENT_WIDGETACTION:
            if (event.source == treeview)
            {
                node = event.extra->As<Widget>();
                node->SetText("Action!!!");
              
              	//Force redraw
                treeview->SelectNode(node,false);
                treeview->SelectNode(node,true);
            }
            break;
        case EVENT_WINDOWCLOSE:
            return 0;
            break;
        }
    }
    return 0;
}

 

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Hey there, you supplied this code for selecting a node:

auto treeview = CreateTreeView(10, 10, sz.x - 20, sz.y - 20, ui->root, TREEVIEW_DRAGANDDROP | TREEVIEW_DRAGINSERT);
treeview->SetLayout(1, 1, 1, 1);

auto node = treeview->root->AddNode("Node 1");
node->AddNode("Subnode 1");
node->AddNode("Subnode 2");
node->AddNode("Subnode 3");

treeview->Draw(0, 0, 0, 0);
treeview->SelectNode(node);

including the temporal fix to make selectnode work. Now I'm trying to select one of the subnodes like this:

auto treeview = CreateTreeView(10, 10, sz.x - 20, sz.y - 20, ui->root, TREEVIEW_DRAGANDDROP | TREEVIEW_DRAGINSERT);
treeview->SetLayout(1, 1, 1, 1);

auto node = treeview->root->AddNode("Node 1");
auto subnode = node->AddNode("Subnode 1");
node->AddNode("Subnode 2");
node->AddNode("Subnode 3");

treeview->Draw(0, 0, 0, 0);
treeview->SelectNode(subnode);

which yields a "vector subscript out of range" exception. Is this a bug too, or do I have to change the code to select one of the subnodes?

I need this to re-select a node with a certain text after recreating the treeview from my data structures. In my own code it also works with the rootnode but not with subnodes like in the example above.

Link to comment
Share on other sites

You just need to make sure the node being selected has been drawn in the last paint event:

    node->Expand();
    treeview->Draw(0, 0, 0, 0);
    treeview->SelectNode(subnode);

This will be fixed in the next build that goes out.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Hi there,

I've discovered another small thing I wanted to let you know about and maybe there is a way to fix this. So I have several buttons which get enabled and disabled depending on what type of item is selected in my treeview. One button is for adding files. In the event handler of that button I call the RequestFile() function which opens the popup dialog. After a file has been selected the dialog closes and the new item is added to my data structure. The tree then rebuilts itself to reflect the changes and call the function to enable/disable buttons. At this time, sometimes the add file button still has the blue border as if the mouse was hovering over it, yet, the mouse has been moved away the moment the popup dialog opened. During the popup dialog, which disabled the main window, the button also stays in this hover state, having the blue border. Now when my code disables the button, it still retains the blue border, but now since it is disabled, it won't go away even when hovering over and then leaving it with the mouse.

I managed to fix this by adding

btn->SetState(false);
btn->Redraw();

before

btn->Disable();

for now. Not sure if both are needed, but it works right now.

Maybe you could add code to make the buttons lose the hover state when disabling them? Or is what I do here like .. a fringe case and not worth adding to the library?

  • Thanks 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...