Files
bsnes/hiro/gtk/widget/tab-frame.cpp
Tim Allen 4a069761f9 Update to v094r11 release.
byuu says:

I've hooked up the input subsystem, and the input manager to assign
hotkeys.

So far I only have digital buttons working (keyboard only), and I'm not
planning on supporting input groups again (mapping multiple physical
buttons to one emulated button), but it's progress. As with the rest of
tomoko, the code's a lot more compact. The nice thing about redoing code
so many times is that each time you get a little bit better at it.

The input configuration is saved to ~/.config/tomoko/settings.bml (just
realized that I'm an idiot and need to rename it to input.bml)

Also hooked up game saves and cartridge unloading. Active controller
changing isn't hooked up yet, and I'll probably do it differently.

Oh, and I declared the ruby lines for other platforms.

Still need to add Cydrak's Windows compilation fixes. I am nothing if
not lazy :P
2015-03-03 21:26:44 +11:00

277 lines
8.8 KiB
C++

namespace hiro {
static auto TabFrame_change(GtkNotebook* notebook, GtkWidget* page, unsigned position, pTabFrame* p) -> void {
p->state().selected = position;
p->_synchronizeLayout();
if(!p->locked()) p->self().doChange();
}
static auto TabFrame_close(GtkButton* button, pTabFrame* p) -> void {
maybe<unsigned> position;
for(auto n : range(p->tabs)) {
if(button == (GtkButton*)p->tabs[n].close) {
position = n;
break;
}
}
if(position) {
if(!p->locked()) p->self().doClose(p->self().item(*position));
}
}
static auto TabFrame_move(GtkNotebook* notebook, GtkWidget* page, unsigned moveTo, pTabFrame* p) -> void {
p->state().selected = gtk_notebook_get_current_page(notebook);
maybe<unsigned> moveFrom;
for(auto n : range(p->tabs)) {
if(page == p->tabs[n].child) {
moveFrom = n;
break;
}
}
if(moveFrom) {
p->state().items.insert(moveTo, p->state().items.take(*moveFrom));
p->tabs.insert(moveTo, p->tabs.take(*moveFrom));
if(!p->locked()) p->self().doMove(p->self().item(*moveFrom), p->self().item(moveTo));
}
}
auto pTabFrame::construct() -> void {
gtkWidget = gtk_notebook_new();
gtk_notebook_set_show_border(GTK_NOTEBOOK(gtkWidget), false);
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkWidget), GTK_POS_TOP);
tabs.reset(); //todo: memory leak, need to release each tab
for(auto& item : state().items) append(item);
setEdge(state().edge);
setItemSelected(state().selected);
g_signal_connect(G_OBJECT(gtkWidget), "page-reordered", G_CALLBACK(TabFrame_move), (gpointer)this);
g_signal_connect(G_OBJECT(gtkWidget), "switch-page", G_CALLBACK(TabFrame_change), (gpointer)this);
pWidget::construct();
}
auto pTabFrame::destruct() -> void {
gtk_widget_destroy(gtkWidget);
}
auto pTabFrame::append(sTabFrameItem item) -> void {
lock();
Tab tab;
tab.child = gtk_fixed_new();
tab.container = gtk_hbox_new(false, 0);
tab.image = gtk_image_new();
tab.title = gtk_label_new("");
gtk_misc_set_alignment(GTK_MISC(tab.title), 0.0, 0.5);
tab.close = gtk_button_new_with_label("\u00d7"); //Unicode multiplication sign (looks better than 'X')
gtk_button_set_focus_on_click(GTK_BUTTON(tab.close), false);
gtk_button_set_relief(GTK_BUTTON(tab.close), GTK_RELIEF_NONE);
pFont::setFont(tab.close, Font::sans(9, "Bold"));
auto color = CreateColor({255, 0, 0});
gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(tab.close)), GTK_STATE_PRELIGHT, &color);
tabs.append(tab);
gtk_widget_show(tab.child);
gtk_widget_show(tab.container);
gtk_widget_show(tab.image);
gtk_widget_show(tab.title);
gtk_widget_show(tab.close);
gtk_box_pack_start(GTK_BOX(tab.container), tab.image, false, false, 0);
gtk_box_pack_start(GTK_BOX(tab.container), tab.title, true, true, 0);
gtk_box_pack_start(GTK_BOX(tab.container), tab.close, false, false, 0);
g_signal_connect(G_OBJECT(tab.close), "clicked", G_CALLBACK(TabFrame_close), (gpointer)this);
gtk_notebook_append_page(GTK_NOTEBOOK(gtkWidget), tab.child, tab.container);
setFont(self().font(true));
setItemMovable(item->offset(), item->movable());
_synchronizeTab(tabs.size() - 1);
setGeometry(self().geometry());
unlock();
}
auto pTabFrame::container(mWidget& widget) -> GtkWidget* {
//TabFrame holds multiple TabFrameItem controls
//each TabFrameItem has its own GtkWindow; plus its own layout
//we need to recurse up from the widget to its topmost layout before the TabFrameItem
//once we know the topmost layout, we search through all TabFrameItems for a match
mObject* object = &widget;
while(object) {
if(object->parentTabFrameItem()) break;
if(auto layout = object->parentLayout()) { object = layout; continue; }
break;
}
unsigned position = 0;
for(auto& item : state().items) {
if(item->state.layout.data() == object) return tabs[position].child;
position++;
}
return nullptr;
}
auto pTabFrame::remove(sTabFrameItem item) -> void {
lock();
//if we are removing the current tab, we have to select another tab manually
if(item->offset() == gtk_notebook_get_current_page(GTK_NOTEBOOK(gtkWidget))) {
//the new tab will be the one after this one
unsigned displacement = 1;
//... unless it's the last tab, in which case it's the one before it
if(item->offset() == self().items() - 1) displacement = -1;
//... unless there are no tabs left, in which case nothing is selected
if(self().items() > 1) {
setItemSelected(item->offset() + displacement);
}
}
tabs.remove(item->offset());
gtk_notebook_remove_page(GTK_NOTEBOOK(gtkWidget), item->offset());
state().selected = gtk_notebook_get_current_page(GTK_NOTEBOOK(gtkWidget));
unlock();
}
auto pTabFrame::setEdge(Edge edge) -> void {
GtkPositionType type;
switch(edge) { default:
case Edge::Top: type = GTK_POS_TOP; break;
case Edge::Bottom: type = GTK_POS_BOTTOM; break;
case Edge::Left: type = GTK_POS_LEFT; break;
case Edge::Right: type = GTK_POS_RIGHT; break;
}
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(gtkWidget), type);
setGeometry(self().geometry());
}
auto pTabFrame::setEnabled(bool enabled) -> void {
for(auto& item : state().items) {
if(auto layout = item->state.layout) {
if(layout->self()) layout->self()->setEnabled(layout->enabled(true));
}
}
pWidget::setEnabled(enabled);
}
auto pTabFrame::setFont(const string& font) -> void {
for(auto n : range(tabs.size())) {
pFont::setFont(tabs[n].title, font);
if(auto layout = state().items[n]->state.layout) {
if(layout->self()) layout->self()->setFont(layout->font(true));
}
}
}
auto pTabFrame::setGeometry(Geometry geometry) -> void {
pWidget::setGeometry(geometry);
geometry.setPosition(0, 0);
if(state().edge == Edge::Top || state().edge == Edge::Bottom) {
geometry.setWidth(geometry.width() - 6);
geometry.setHeight(geometry.height() - (15 + _tabHeight()));
} else {
geometry.setWidth(geometry.width() - (17 + _tabWidth()));
geometry.setHeight(geometry.height() - 6);
}
for(auto& item : state().items) {
if(item->state.layout) item->state.layout->setGeometry(geometry);
}
}
auto pTabFrame::setItemClosable(unsigned position, bool closable) -> void {
_synchronizeTab(position);
}
auto pTabFrame::setItemIcon(unsigned position, const image& icon) -> void {
_synchronizeTab(position);
}
auto pTabFrame::setItemLayout(unsigned position, shared_pointer<mLayout> layout) -> void {
//if(layout->self()) layout->self()->setParent();
}
auto pTabFrame::setItemMovable(unsigned position, bool movable) -> void {
lock();
gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(gtkWidget), tabs[position].child, movable);
unlock();
}
auto pTabFrame::setItemSelected(unsigned position) -> void {
lock();
gtk_notebook_set_current_page(GTK_NOTEBOOK(gtkWidget), position);
unlock();
}
auto pTabFrame::setItemText(unsigned position, const string& text) -> void {
_synchronizeTab(position);
}
auto pTabFrame::setVisible(bool visible) -> void {
for(auto& item : state().items) {
if(auto layout = item->state.layout) {
if(layout->self()) {
layout->self()->setVisible(layout->visible(true));
}
}
}
pWidget::setVisible(visible);
}
auto pTabFrame::_synchronizeLayout() -> void {
unsigned position = 0;
for(auto& item : state().items) {
if(auto layout = item->state.layout) {
if(layout->self()) {
layout->self()->setVisible(layout->visible(true) && position == state().selected);
}
}
position++;
}
}
auto pTabFrame::_synchronizeTab(unsigned position) -> void {
auto& item = state().items[position];
auto& tab = tabs[position];
gtk_widget_set_visible(tab.close, item->closable());
if(auto copy = item->state.icon) {
unsigned size = pFont::size(self().font(true), " ").height();
copy.scale(size, size);
auto pixbuf = CreatePixbuf(copy);
gtk_image_set_from_pixbuf(GTK_IMAGE(tab.image), pixbuf);
} else {
gtk_image_clear(GTK_IMAGE(tab.image));
}
string text = {
item->state.icon && item->state.text ? " " : "",
item->state.text,
item->state.text && item->state.closable ? " " : ""
};
gtk_label_set_text(GTK_LABEL(tab.title), text);
}
//compute the height of the tallest tab for child layout geometry calculations
auto pTabFrame::_tabHeight() -> unsigned {
signed height = 1;
for(auto n : range(self().items())) {
height = max(height, tabs[n].image->allocation.height);
height = max(height, tabs[n].title->allocation.height);
if(!state().items[n]->closable()) continue;
height = max(height, tabs[n].close->allocation.height);
}
return height;
}
auto pTabFrame::_tabWidth() -> unsigned {
signed width = 1;
for(auto n : range(self().items())) {
width = max(width, tabs[n].image->allocation.width + tabs[n].title->allocation.width +
(state().items[n]->closable() ? tabs[n].close->allocation.width : 0)
);
}
return width;
}
}