From c2d43d59f92e51414b380a5101a9db72c1374fb2 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Mon, 21 Apr 2025 19:33:52 +0200 Subject: [PATCH] update toolkit --- ui/cocoa/EventData.h | 3 +- ui/cocoa/MainWindow.h | 30 ++++++- ui/cocoa/MainWindow.m | 200 +++++++++++++++++++++++++++++++++++++++++- ui/cocoa/button.m | 7 +- ui/cocoa/menu.h | 14 +++ ui/cocoa/menu.m | 63 ++++++++++++- ui/cocoa/window.m | 8 ++ ui/qt/list.cpp | 75 ++++++++++++++++ ui/qt/list.h | 40 +++++++++ ui/qt/model.cpp | 112 +++++++++++++++++++++++ ui/qt/model.h | 42 +++++++++ ui/qt/qt5.pro | 4 +- ui/qt/text.cpp | 6 +- ui/qt/toolbar.cpp | 98 +++++++++++++++++++++ ui/qt/toolbar.h | 6 +- ui/qt/window.cpp | 6 +- 16 files changed, 697 insertions(+), 17 deletions(-) create mode 100644 ui/qt/list.cpp create mode 100644 ui/qt/list.h diff --git a/ui/cocoa/EventData.h b/ui/cocoa/EventData.h index 8da0ee4..4ee89f3 100644 --- a/ui/cocoa/EventData.h +++ b/ui/cocoa/EventData.h @@ -47,6 +47,5 @@ typedef void(*get_eventdata_func)(id sender, UiVar *var, void **eventdata, int * - (void)handleEventWithEventData:(id)sender; -- (SEL)addDynamicMethod:(unsigned long long)method_id; - @end + diff --git a/ui/cocoa/MainWindow.h b/ui/cocoa/MainWindow.h index 50e2eef..aab8a70 100644 --- a/ui/cocoa/MainWindow.h +++ b/ui/cocoa/MainWindow.h @@ -31,8 +31,36 @@ @interface MainWindow : NSWindow +- (MainWindow*)init:(UiObject*)obj; + +@end + + +@interface MainWindowController : NSWindowController + @property UiObject *uiobj; +@property NSMutableDictionary *checkItemStates; +@property NSMutableDictionary *radioItems; -- (MainWindow*)init:(UiObject*)obj; +- (MainWindowController*)initWithWindow:(UiObject*)obj window:(NSWindow*)window; + +- (void) windowDidLoad; +- (void)menuItemAction:(id)sender; + +- (BOOL) validateMenuItem:(NSMenuItem *) menuItem; + +@end + +@interface MenuItemState : NSObject +@property (weak) MainWindowController *mainWindow; +@property UiVar *var; +@property int state; @end + + +int64_t ui_menu_check_item_get(UiInteger *i); +void ui_menu_check_item_set(UiInteger *i, int64_t value); + +int64_t ui_menu_radio_item_get(UiInteger *i); +void ui_menu_radio_item_set(UiInteger *i, int64_t value); diff --git a/ui/cocoa/MainWindow.m b/ui/cocoa/MainWindow.m index 232f93f..27f34fd 100644 --- a/ui/cocoa/MainWindow.m +++ b/ui/cocoa/MainWindow.m @@ -30,11 +30,14 @@ #import "Container.h" #import "GridLayout.h" #import "../common/object.h" +#import + +#import "EventData.h" +#import "menu.h" @implementation MainWindow - (MainWindow*)init:(UiObject*)obj { - self.uiobj = obj; NSRect frame = NSMakeRect(300, 200, 600, 500); self = [self initWithContentRect:frame @@ -63,3 +66,198 @@ } @end + + + +@implementation MainWindowController + +- (MainWindowController*)initWithWindow:(UiObject*)obj window:(NSWindow*)window { + self = [super initWithWindow:window]; + _uiobj = obj; + + self.checkItemStates = [[NSMutableDictionary alloc] init]; + self.radioItems = [[NSMutableDictionary alloc] init]; + + // bind all stateful menu items (checkbox, radiobuttons, lists) + NSArray *menuBindItems = ui_get_binding_items(); // returns all items that require binding + for(MenuItem *item in menuBindItems) { + if(item.checkItem || item.radioItem) { + // simple check item (ui_menu_toggleitem_create) + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, NULL, item.checkItem ? item.checkItem->varname : item.radioItem->varname, UI_VAR_INTEGER); + // create the state object for this item/window + MenuItemState *state = [[MenuItemState alloc] init]; + state.mainWindow = self; + state.var = var; + if(var) { + UiInteger *i = var->value; + if(item.checkItem) { + // bind toggle item + state.state = (int)i->value; + i->obj = (__bridge void*)state; + i->get = ui_menu_check_item_get; + i->set = ui_menu_check_item_set; + } else { + // bind radio item + NSMutableArray *rgroup = nil; + if(i->obj) { + rgroup = (__bridge NSMutableArray*)i->obj; + } else { + // create a new rgroup array and register it in the window + rgroup = [[NSMutableArray alloc] init]; + NSString *varname = [[NSString alloc] initWithUTF8String:item.radioItem->varname]; + [_radioItems setObject:rgroup forKey:varname]; + i->obj = (__bridge void*)rgroup; + } + i->get = ui_menu_radio_item_get; + i->set = ui_menu_radio_item_set; + [rgroup addObject:state]; // add this item state to the radio group + // i->value can contain a non-zero value, which means a specific radiobutton + // should be pre-selected + if(i->value == rgroup.count) { + state.state = NSControlStateValueOn; + } + } + } else { + state.state = 0; + } + [_checkItemStates setObject:state forKey:item.itemId]; + } + } + + return self; +} + +- (void) windowDidLoad { + [self.window setNextResponder:self]; +} + +- (void)menuItemAction:(id)sender { + EventData *event = objc_getAssociatedObject(sender, "eventdata"); + if(event) { + event.obj = self.uiobj; // temporary set the event object + [event handleEvent:sender]; + } +} + +- (void)menuCheckItemAction:(id)sender { + NSMenuItem *menuItem = sender; + MenuItem *item = objc_getAssociatedObject(sender, "menuitem"); + if(!item || !item.checkItem) { + return; + } + + MenuItemState *state = [_checkItemStates objectForKey:item.itemId]; + state.state = state.state == NSControlStateValueOff ? NSControlStateValueOn : NSControlStateValueOff; + menuItem.state = state.state; + + UiMenuCheckItem *it = item.checkItem; + if(it->callback) { + UiEvent event; + event.obj = _uiobj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = state.var ? state.var->value : NULL; + event.intval = state.state; + it->callback(&event, it->userdata); + } +} + +- (void)menuRadioItemAction:(id)sender { + NSMenuItem *menuItem = sender; + MenuItem *item = objc_getAssociatedObject(sender, "menuitem"); + if(!item || !item.radioItem) { + return; + } + + UiMenuRadioItem *it = item.radioItem; + if(!it->varname) { + return; + } + + MenuItemState *state = [_checkItemStates objectForKey:item.itemId]; // current state of this menu item + + NSString *varname = [[NSString alloc] initWithUTF8String:it->varname]; + NSArray *radioGroup = [_radioItems objectForKey:varname]; + if(!radioGroup) { + return; + } + int index = 1; + int value = 0; + for(MenuItemState *g in radioGroup) { + if(g == state) { + menuItem.state = NSControlStateValueOn; + g.state = NSControlStateValueOn; + value = index; + } else { + menuItem.state = NSControlStateValueOff; + g.state = NSControlStateValueOff; + } + } + + if(it->callback) { + UiEvent event; + event.obj = _uiobj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = state.var ? state.var->value : NULL; + event.intval = value; + it->callback(&event, it->userdata); + } +} + + +- (BOOL) validateMenuItem:(NSMenuItem *) menuItem { + MenuItem *item = objc_getAssociatedObject(menuItem, "menuitem"); + if(item) { + MenuItemState *state = [_checkItemStates objectForKey:item.itemId]; + if(state) { + menuItem.state = state.state; + } else { + menuItem.state = NSControlStateValueOff; + } + } + + return YES; +} + +@end + +@implementation MenuItemState + +@end + +int64_t ui_menu_check_item_get(UiInteger *i) { + MenuItemState *state = (__bridge MenuItemState*)i->obj; + i->value = state.state; + return i->value; +} + +void ui_menu_check_item_set(UiInteger *i, int64_t value) { + MenuItemState *state = (__bridge MenuItemState*)i->obj; + i->value = value; + state.state = (int)value; +} + +int64_t ui_menu_radio_item_get(UiInteger *i) { + NSArray *rgroup = (__bridge NSArray*)i->obj; + i->value = 0; + int index = 1; + for(MenuItemState *state in rgroup) { + if(state.state == NSControlStateValueOn) { + i->value = index; + break; + } + index++; + } + return i->value; +} + +void ui_menu_radio_item_set(UiInteger *i, int64_t value) { + NSArray *rgroup = (__bridge NSArray*)i->obj; + i->value = 0; + int index = 1; + for(MenuItemState *state in rgroup) { + state.state = value == index; + index++; + } +} diff --git a/ui/cocoa/button.m b/ui/cocoa/button.m index cd2f245..e819fb0 100644 --- a/ui/cocoa/button.m +++ b/ui/cocoa/button.m @@ -252,17 +252,17 @@ int64_t ui_radiobuttons_get(UiInteger *i) { int64_t index = 0; for(UiRadioButton *b in buttons) { if([b cell].state != 0) { - i->value = index; + i->value = index + 1; break; } index++; } - return index; + return i->value; } void ui_radiobuttons_set(UiInteger *i, int64_t value) { NSMutableArray *buttons = (__bridge NSMutableArray*)i->obj; - int64_t index = 0; + int64_t index = 1; for(UiRadioButton *b in buttons) { if(index == value) { [b cell].state = NSControlStateValueOn; @@ -271,5 +271,4 @@ void ui_radiobuttons_set(UiInteger *i, int64_t value) { } index++; } - return index; } diff --git a/ui/cocoa/menu.h b/ui/cocoa/menu.h index 9836fa3..be58c9f 100644 --- a/ui/cocoa/menu.h +++ b/ui/cocoa/menu.h @@ -31,6 +31,18 @@ #import "../common/menu.h" +@interface MenuItem : NSObject + +@property (strong) NSString *itemId; +@property UiMenuCheckItem *checkItem; +@property UiMenuRadioItem *radioItem; +@property ui_callback callback; +@property void *userdata; + +- (MenuItem*)init:(int)itId; + +@end + void ui_menu_init(void); typedef void(*ui_menu_add_f)(NSMenu*, int, UiMenuItemI*); @@ -42,3 +54,5 @@ void add_checkitem_widget(NSMenu *parent, int i, UiMenuItemI *item); void add_radioitem_widget(NSMenu *parent, int index, UiMenuItemI *item); void add_checkitemnv_widget(NSMenu *parent, int i, UiMenuItemI *item); void add_menuitem_list_widget(NSMenu *parent, int i, UiMenuItemI *item); + +NSArray* ui_get_binding_items(void); diff --git a/ui/cocoa/menu.m b/ui/cocoa/menu.m index 10dd588..5395d0a 100644 --- a/ui/cocoa/menu.m +++ b/ui/cocoa/menu.m @@ -30,9 +30,27 @@ #import #import #import +#import #import "menu.h" #import "window.h" +#import "EventData.h" + +#pragma GCC diagnostic ignored "-Wundeclared-selector" +#pragma clang diagnostic ignored "-Wundeclared-selector" + +// holds all items that need bindings +// value type: MenuItem* +static NSMutableArray *bindingItems; + +@implementation MenuItem + +- (MenuItem*)init:(int)itId { + self.itemId = [[NSString alloc] initWithFormat:@"item%d", itId]; + return self; +} + +@end static ui_menu_add_f createMenuItem[] = { /* UI_MENU */ add_menu_widget, @@ -67,20 +85,51 @@ void add_menu_widget(NSMenu *parent, int i, UiMenuItemI *item) { void add_menuitem_widget(NSMenu *parent, int i, UiMenuItemI *item) { UiMenuItem *it = (UiMenuItem*)item; + NSString *str = [[NSString alloc] initWithUTF8String:it->label]; - NSMenuItem *menuItem = [parent addItemWithTitle:str action:nil keyEquivalent:@""]; + NSMenuItem *menuItem = [parent addItemWithTitle:str action:@selector(menuItemAction:) keyEquivalent:@""]; + + if(it->callback) { + EventData *event = [[EventData alloc] init:it->callback userdata:it->userdata]; + objc_setAssociatedObject(menuItem, "eventdata", event, OBJC_ASSOCIATION_RETAIN); + } } void add_menuseparator_widget(NSMenu *parent, int i, UiMenuItemI *item) { - + NSMenuItem *menuItem = [NSMenuItem separatorItem]; + [parent addItem:menuItem]; } +static int nItem = 0; + void add_checkitem_widget(NSMenu *parent, int i, UiMenuItemI *item) { + UiMenuCheckItem *it = (UiMenuCheckItem*)item; + + NSString *str = [[NSString alloc] initWithUTF8String:it->label]; + NSMenuItem *menuItem = [parent addItemWithTitle:str action:@selector(menuCheckItemAction:) keyEquivalent:@""]; + MenuItem *mItem = [[MenuItem alloc] init:nItem++]; + mItem.callback = it->callback; + mItem.userdata = it->userdata; + mItem.checkItem = it; + + objc_setAssociatedObject(menuItem, "menuitem", mItem, OBJC_ASSOCIATION_RETAIN); + [bindingItems addObject:mItem]; } void add_radioitem_widget(NSMenu *parent, int index, UiMenuItemI *item) { + UiMenuRadioItem *it = (UiMenuRadioItem*)item; + + NSString *str = [[NSString alloc] initWithUTF8String:it->label]; + NSMenuItem *menuItem = [parent addItemWithTitle:str action:@selector(menuRadioItemAction:) keyEquivalent:@""]; + MenuItem *mItem = [[MenuItem alloc] init:nItem++]; + mItem.callback = it->callback; + mItem.userdata = it->userdata; + mItem.radioItem = it; + + objc_setAssociatedObject(menuItem, "menuitem", mItem, OBJC_ASSOCIATION_RETAIN); + [bindingItems addObject:mItem]; } void add_checkitemnv_widget(NSMenu *parent, int i, UiMenuItemI *item) { @@ -93,17 +142,25 @@ void add_menuitem_list_widget(NSMenu *parent, int i, UiMenuItemI *item) { void ui_menu_init(void) { + bindingItems = [[NSMutableArray alloc] init]; + UiMenu *menus_begin = uic_get_menu_list(); UiMenu *ls = menus_begin; + int index = 1; while(ls) { if(ls->item.type == UI_MENU) { NSString *str = [[NSString alloc] initWithUTF8String:ls->label]; NSMenu *menu = [[NSMenu alloc] initWithTitle: str]; - NSMenuItem *menuItem = [[NSApp mainMenu] addItemWithTitle:str action:nil keyEquivalent:@""]; + NSMenuItem *menuItem = [[NSApp mainMenu] insertItemWithTitle:str action:nil keyEquivalent:@"" atIndex:index]; [[NSApp mainMenu] setSubmenu:menu forItem:menuItem]; add_menu_items(menu, 0, ls); } ls = (UiMenu*)ls->item.next; + index++; } } + +NSArray* ui_get_binding_items(void) { + return bindingItems; +} diff --git a/ui/cocoa/window.m b/ui/cocoa/window.m index a04537e..8e2c81f 100644 --- a/ui/cocoa/window.m +++ b/ui/cocoa/window.m @@ -31,6 +31,8 @@ #import "MainWindow.h" #import "WindowManager.h" +#import + #include "../ui/window.h" #include "../ui/properties.h" #include "../common/context.h" @@ -39,6 +41,7 @@ #include + static UiObject* create_window(const char *title, BOOL simple) { CxMempool *mp = cxMempoolCreateSimple(256); UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); @@ -52,6 +55,11 @@ static UiObject* create_window(const char *title, BOOL simple) { obj->wobj = (__bridge void*)window; + MainWindowController *controller = [[MainWindowController alloc] initWithWindow:obj window:window]; + window.windowController = controller; + [window setNextResponder:(NSResponder*)controller]; + objc_setAssociatedObject(window, "windowcontroller", controller, OBJC_ASSOCIATION_RETAIN); + return obj; } diff --git a/ui/qt/list.cpp b/ui/qt/list.cpp new file mode 100644 index 0000000..3253ca7 --- /dev/null +++ b/ui/qt/list.cpp @@ -0,0 +1,75 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "list.h" +#include "container.h" + +#include +#include +#include + +extern "C" void* ui_strmodel_getvalue(void *elm, int column) { + return column == 0 ? elm : NULL; +} + + +UIWIDGET ui_listview_create(UiObject* obj, UiListArgs args) { + UiContainerPrivate *ctn = ui_obj_container(obj); + UI_APPLY_LAYOUT(ctn->layout, args); + + QListView *view = new QListView(); + + ui_getvaluefunc getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue; + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.list, args.varname, UI_VAR_LIST); + + ListModel *model = new ListModel(obj, view, var, getvalue); + view->setModel(model); + + if(var) { + UiList *list = (UiList*)var->value; + list->update = ui_listmodel_update; + list->getselection = ui_listmodel_getselection; + list->setselection = ui_listmodel_setselection; + list->obj = model; + } + + model->setActivationCallback(args.onactivate, args.onactivatedata); + model->setSelectionCallback(args.onselection, args.onselectiondata); + + QItemSelectionModel *s = view->selectionModel(); + QObject::connect( + s, + SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + model, + SLOT(selectionChanged(const QItemSelection &, const QItemSelection &))); + + + ctn->add(view, false); + + return view; +} diff --git a/ui/qt/list.h b/ui/qt/list.h new file mode 100644 index 0000000..664b32b --- /dev/null +++ b/ui/qt/list.h @@ -0,0 +1,40 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TREE_H +#define TREE_H + +#include "../ui/tree.h" +#include "model.h" + +#include + + + +#endif /* TREE_H */ + diff --git a/ui/qt/model.cpp b/ui/qt/model.cpp index d6ead68..1ac4574 100644 --- a/ui/qt/model.cpp +++ b/ui/qt/model.cpp @@ -28,3 +28,115 @@ #include "model.h" + +ListModel::ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc getvalue){ + this->obj = obj; + this->view = view; + this->var = var; + this->getvalue = getvalue; + this->onactivate = nullptr; + this->onactivatedata = nullptr; + this->onselection = nullptr; + this->onselectiondata = nullptr; +} + +void ListModel::setActivationCallback(ui_callback f, void *userdata) { + onactivate = f; + onactivatedata = userdata; +} + +void ListModel::setSelectionCallback(ui_callback f, void *userdata) { + onselection = f; + onselectiondata = userdata; +} + +void ListModel::update(int row) { + if(row >= 0) { + this->update(row); + } else { + this->beginResetModel(); + this->endResetModel(); + } +} + +int ListModel::rowCount(const QModelIndex& parent) const { + UiList *list = (UiList*)var->value; + return ui_list_count(list); +} + +QVariant ListModel::data(const QModelIndex &index, int role) const { + if(role == Qt::DisplayRole) { + UiList *ls = (UiList*)var->value; + void *rowData = ls->get(ls, index.row()); + if(rowData && getvalue) { + void *value = getvalue(rowData, 0); + return QString::fromUtf8((char*)value); + } + } + return QVariant(); +} + +void ListModel::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { + UiListSelection sel = ui_selection_model_to_selection(view->selectionModel()); + + UiEvent event; + event.obj = obj; + event.window = obj->window; + event.document = obj->ctx->document; + event.eventdata = &sel; + event.intval = sel.count; + event.set = ui_get_setop(); + + if(onactivate) { + onactivate(&event, onactivatedata); + } + if(onselection) { + onselection(&event, onselectiondata); + } + + free(sel.rows); +} + + + + +UiListSelection ui_selection_model_to_selection(QItemSelectionModel *model) { + UiListSelection sel; + sel.rows = NULL; + sel.count = 0; + + if(model->hasSelection()) { + QModelIndexList indices = model->selectedIndexes(); + sel.count = indices.count(); + sel.rows = (int*)calloc(sel.count, sizeof(int)); + + int i = 0; + for (const QModelIndex &index : indices) { + sel.rows[i++] = index.row(); + } + } + + return sel; +} + +/* ---------------------- UiList implementation -----------------------------*/ + +void ui_listmodel_update(UiList *list, int row) { + ListModel *model = (ListModel*)list->obj; + model->update(row); +} + +void ui_listmodel_setselection(UiList *list, UiListSelection sel) { + ListModel *model = (ListModel*)list->obj; + QItemSelection selection; + for (int i=0;iindex(sel.rows[i]); + selection.select(index, index); + } + model->view->selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect); +} + +UiListSelection ui_listmodel_getselection(UiList *list) { + ListModel *model = (ListModel*)list->obj; + return ui_selection_model_to_selection(model->view->selectionModel()); +} diff --git a/ui/qt/model.h b/ui/qt/model.h index d9a707d..e2c2e59 100644 --- a/ui/qt/model.h +++ b/ui/qt/model.h @@ -40,5 +40,47 @@ #include + +class ListModel : public QAbstractListModel { + Q_OBJECT + + ui_getvaluefunc getvalue; + ui_callback onactivate; + void *onactivatedata; + ui_callback onselection; + void *onselectiondata; + +public: + UiObject *obj; + UiVar *var; + QListView *view; + + ListModel(UiObject *obj, QListView *view, UiVar *var, ui_getvaluefunc getvalue); + + void setActivationCallback(ui_callback f, void *userdata); + void setSelectionCallback(ui_callback f, void *userdata); + + void update(int row); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +public slots: + void selectionChanged( + const QItemSelection & selected, + const QItemSelection & deselected); +}; + + +UiListSelection ui_selection_model_to_selection(QItemSelectionModel *model); + +extern "C" { + + void ui_listmodel_update(UiList *list, int row); + void ui_listmodel_setselection(UiList *list, UiListSelection sel); + UiListSelection ui_listmodel_getselection(UiList *list); + +} + #endif /* MODEL_H */ diff --git a/ui/qt/qt5.pro b/ui/qt/qt5.pro index 5971c32..a97ef1c 100644 --- a/ui/qt/qt5.pro +++ b/ui/qt/qt5.pro @@ -45,7 +45,7 @@ SOURCES += stock.cpp SOURCES += container.cpp SOURCES += text.cpp SOURCES += model.cpp -SOURCES += tree.cpp +SOURCES += list.cpp SOURCES += button.cpp SOURCES += label.cpp SOURCES += graphics.cpp @@ -60,7 +60,7 @@ HEADERS += stock.h HEADERS += container.h HEADERS += text.h HEADERS += model.h -HEADERS += tree.h +HEADERS += list.h HEADERS += button.h HEADERS += label.h HEADERS += graphics.h diff --git a/ui/qt/text.cpp b/ui/qt/text.cpp index f900ac0..fb3f656 100644 --- a/ui/qt/text.cpp +++ b/ui/qt/text.cpp @@ -201,7 +201,11 @@ int ui_textarea_length(UiText *text) { } void ui_textarea_remove(UiText *text, int begin, int end) { - // TODO + QTextDocument *doc = (QTextDocument*)text->data1; + QTextCursor cursor(doc); + cursor.setPosition(begin); + cursor.setPosition(end, QTextCursor::KeepAnchor); + cursor.removeSelectedText(); } /* ------------------------------ TextField ------------------------------ */ diff --git a/ui/qt/toolbar.cpp b/ui/qt/toolbar.cpp index 1128585..22e81de 100644 --- a/ui/qt/toolbar.cpp +++ b/ui/qt/toolbar.cpp @@ -32,3 +32,101 @@ #include "menu.h" #include "stock.h" +static void add_items(UiObject *obj, QToolBar *toolbar, CxList *defaults, CxMap *items); +static void create_item(UiObject *obj, QToolBar *toolbar, UiToolbarItemI *i); + +QToolBar* ui_create_toolbar(UiObject *obj) { + CxMap *items = uic_get_toolbar_items(); + CxList *left_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); + CxList *center_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); + CxList *right_defaults = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); + + if(!items || cxMapSize(items) == 0) { + return nullptr; + } + + QToolBar *toolbar = new QToolBar(); + add_items(obj, toolbar, left_defaults, items); + add_items(obj, toolbar, center_defaults, items); + add_items(obj, toolbar, right_defaults, items); + + + return toolbar; +} + +static void add_items(UiObject *obj, QToolBar *toolbar, CxList *defaults, CxMap *items) { + CxIterator i = cxListIterator(defaults); + cx_foreach(char *, name, i) { + UiToolbarItemI *item = (UiToolbarItemI*)cxMapGet(items, name); + if(item) { + create_item(obj, toolbar, item); + } else { + fprintf(stderr, "UI Error: unknown toolbar item '%s'\n", name); + } + } +} + +static void create_item(UiObject *obj, QToolBar *toolbar, UiToolbarItemI *i) { + switch(i->type) { + case UI_TOOLBAR_ITEM: { + ui_toolbar_add_item(obj, toolbar, (UiToolbarItem*)i); + break; + } + case UI_TOOLBAR_TOGGLEITEM: { + ui_toolbar_add_toggleitem(obj, toolbar, (UiToolbarToggleItem*)i); + break; + } + case UI_TOOLBAR_MENU: { + ui_toolbar_add_menu(obj, toolbar, (UiToolbarMenuItem*)i); + break; + } + default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type); + } +} + +void ui_toolbar_add_item(UiObject *obj, QToolBar *toolbar, UiToolbarItem *item) { + QAction *action = new QAction(); + if(item->args.label) { + action->setText(item->args.label); + } + if(item->args.icon) { + action->setIcon(QIcon::fromTheme(item->args.icon)); + } + toolbar->addAction(action); + + UiEventWrapper *event = new UiEventWrapper(obj, item->args.onclick, item->args.onclickdata); + action->connect(action, SIGNAL(triggered()), event, SLOT(slot())); + action->connect(action, SIGNAL(destroyed()), event, SLOT(destroy())); +} + +static void toolbar_togglebutton_event(UiEvent *event, UiEventWrapper *wrapper) { + QAction *action = (QAction*)wrapper->customdata1; + event->intval = action->isChecked(); + if(wrapper->var) { + event->eventdata = wrapper->var->value; + } +} + +void ui_toolbar_add_toggleitem(UiObject *obj, QToolBar *toolbar, UiToolbarToggleItem *item) { + QAction *action = new QAction(); + action->setCheckable(true); + if(item->args.label) { + action->setText(item->args.label); + } + if(item->args.icon) { + action->setIcon(QIcon::fromTheme(item->args.icon)); + } + toolbar->addAction(action); + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, nullptr, item->args.varname, UI_VAR_INTEGER); + UiEventWrapper *event = new UiEventWrapper(obj, item->args.onchange, item->args.onchangedata); + event->var = var; + event->customdata1 = action; + event->prepare_event = toolbar_togglebutton_event; + action->connect(action, SIGNAL(triggered()), event, SLOT(slot())); + action->connect(action, SIGNAL(destroyed()), event, SLOT(destroy())); +} + +void ui_toolbar_add_menu(UiObject *obj, QToolBar *toolbar, UiToolbarMenuItem *item) { + +} diff --git a/ui/qt/toolbar.h b/ui/qt/toolbar.h index 7a4dc2a..749e84f 100644 --- a/ui/qt/toolbar.h +++ b/ui/qt/toolbar.h @@ -31,10 +31,14 @@ #include "toolkit.h" #include "../ui/toolbar.h" +#include "../common/toolbar.h" #include +QToolBar* ui_create_toolbar(UiObject *obj); - +void ui_toolbar_add_item(UiObject *obj, QToolBar *toolbar, UiToolbarItem *item); +void ui_toolbar_add_toggleitem(UiObject *obj, QToolBar *toolbar, UiToolbarToggleItem *item); +void ui_toolbar_add_menu(UiObject *obj, QToolBar *toolbar, UiToolbarMenuItem *item); #endif /* TOOLBAR_H */ diff --git a/ui/qt/window.cpp b/ui/qt/window.cpp index a30dc6b..bab1fa6 100644 --- a/ui/qt/window.cpp +++ b/ui/qt/window.cpp @@ -51,8 +51,10 @@ static UiObject* create_window(const char *title, void *window_data, bool simple if(!simple) { ui_add_menus(obj, window); - //QToolBar *toolbar = ui_create_toolbar(obj); - //window->addToolBar(Qt::TopToolBarArea, toolbar); + QToolBar *toolbar = ui_create_toolbar(obj); + if(toolbar) { + window->addToolBar(Qt::TopToolBarArea, toolbar); + } } QBoxLayout *box = new QVBoxLayout(); -- 2.43.5