Merge branch 'Menus' into Controls

Controls
David Dormagen 2013-05-24 01:24:57 +02:00
commit d18d8c65c9
36 changed files with 2934 additions and 29 deletions

View File

@ -284,6 +284,8 @@ set(OC_CLONK_SOURCES
src/gui/C4MessageBoard.h
src/gui/C4MessageInput.cpp
src/gui/C4MessageInput.h
src/gui/C4GuiWindow.cpp
src/gui/C4GuiWindow.h
src/gui/C4MouseControl.cpp
src/gui/C4MouseControl.h
src/gui/C4PlayerInfoListBox.cpp
@ -1152,7 +1154,7 @@ endif()
set_property(TARGET openclonk APPEND PROPERTY COMPILE_DEFINITIONS GLEW_STATIC)
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG _DEBUG)
# This expands some variables in Info.plist as a side-effect. XCode might then
# This expands some variables in Info.plist as a side-effect. XCode might then
# expand a second time, using the same syntax. Try not to get confused by this!
set_target_properties(openclonk PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/src/res/Info.plist")
@ -1253,10 +1255,10 @@ if (APPLE)
add_custom_command(TARGET openclonk
POST_BUILD COMMAND "/usr/bin/ruby" "${CMAKE_CURRENT_SOURCE_DIR}/tools/osx_bundle_libs"
)
set(CMAKE_XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER YES)
set(CMAKE_XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/C4Include.h")
SET_TARGET_PROPERTIES(openclonk PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER YES)
SET_TARGET_PROPERTIES(openclonk PROPERTIES XCODE_ATTRIBUTE_GCC_PFE_FILE_C_DIALECTS "c++ objective-c++")
SET_TARGET_PROPERTIES(openclonk PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/C4Include.h")
@ -1267,7 +1269,7 @@ if (APPLE)
SET_TARGET_PROPERTIES(libmisc PROPERTIES XCODE_ATTRIBUTE_GCC_PFE_FILE_C_DIALECTS "c++ objective-c++")
SET_TARGET_PROPERTIES(libmisc PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/C4Include.h")
SET_TARGET_PROPERTIES(openclonk PROPERTIES XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES)
if (USE_APPLE_CLANG)
set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvm.clang.1_0")
set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++0x")
@ -1275,7 +1277,7 @@ if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++0x -stdlib=libc++ -g -Wall")
SET_TARGET_PROPERTIES(c4group PROPERTIES XCODE_ATTRIBUTE_GCC_PFE_FILE_C_DIALECTS "c++0x objective-c++0x")
SET_TARGET_PROPERTIES(openclonk PROPERTIES XCODE_ATTRIBUTE_GCC_PFE_FILE_C_DIALECTS "c++0x objective-c++0x")
set(HAVE_RVALUE_REF ON)
else()
set(CMAKE_XCODE_ATTRIBUTE_GCC_VERSION "com.apple.compilers.llvmgcc42")

View File

@ -319,6 +319,8 @@ src/gui/C4MessageBoard.cpp \
src/gui/C4MessageBoard.h \
src/gui/C4MessageInput.cpp \
src/gui/C4MessageInput.h \
src/gui/C4GuiWindow.cpp \
src/gui/C4GuiWindow.h \
src/gui/C4MouseControl.cpp \
src/gui/C4MouseControl.h \
src/gui/C4PlayerInfoListBox.cpp \

View File

@ -0,0 +1,7 @@
[DefCore]
id=HUD_MenuStyle_Classic
Version=4,10,0,0
Category=C4D_StaticBack
Width=1
Height=1
Offset=-1,-1

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

View File

@ -0,0 +1,104 @@
/**
Classic
Mimics the interface to a classic menu.
*/
local Name = "Classic Menu";
// set on creation
local menu_def, permanent;
local menu_layout;
local target;
// set later
local entries;
func Construction()
{
entries = [];
}
global func CreateClassicMenu(id symbol, object command_object, int extra, string caption, int extra_data, int style, bool permanent, id menu_id)
{
if (!this) return;
var menu = CreateObject(HUD_MenuStyle_Classic, 0, 0, GetOwner());
menu.Visibility = VIS_Owner;
menu.menu_def = menu_id;
menu.permanent = permanent;
menu.target = this;
menu.menu_layout =
{
BackgroundColor = 0x50553300,
Decoration = GUI_MenuDeco,
Target = menu,
inner =
{
header =
{
Hgt = [0, 32],
icon = {Symbol = symbol, Wdt = [0, 32], Hgt = [0, 32]},
caption = {X = [0, 32], Text = caption, Style = GUI_TextVCenter}
},
body =
{
Y = [0, 32],
items =
{
Wdt = 500,
Style = GUI_GridLayout
},
description =
{
ID = 1,
Target = 0,
X = 500,
Text = "Empty"
},
}
}
};
Gui_AddMargin(menu.menu_layout.inner, 15, 15);
return menu;
}
public func AddMenuItem(string caption, string command, symbol, int count, parameter, string info_caption, int extra, XPar1, XPar2)
{
var ID = GetLength(entries) + 1;
var entry =
{
Target = target, // needed for the call
ID = ID,
BackgroundColor = {Std = 0, Hover = 0x50ff0000},
Symbol = symbol,
Wdt = [0, 64],
Hgt = [0, 64],
Text = Format("%dx", count),
Priority = ID,
OnClick = GuiAction_Call(this, "OnClick", [symbol, ID, command, parameter]),
OnMouseIn = [GuiAction_SetTag(nil, 0, "Hover"), GuiAction_Call(this, "UpdateDesc")],
OnMouseOut = GuiAction_SetTag(nil, 0, "Std"),
};
entries[ID] = [info_caption ?? symbol.Description];
menu_layout.inner.body.items[Format("child%d", ID)] = entry;
return entry;
}
func Open()
{
return CustomGuiOpen(menu_layout);
}
func UpdateDesc(int player, int ID, int subwindowID, object target, data)
{
var update = { Text = entries[subwindowID][0] };
CustomGuiUpdate(update, ID, 1, 0);
}
func OnClick(int player, int ID, int subwindowID, object target, data)
{
target->Call(data[2], data[0], data[3]);
if (!permanent)
CustomGuiClose(ID);
}

View File

@ -0,0 +1,2 @@
Name=Classic
Description=Imitiert ein Interface ähnlich des klassischen Menüs.

View File

@ -0,0 +1,2 @@
Name=Classic
Description=Mimics the interface to a classic menu.

View File

@ -0,0 +1,7 @@
[DefCore]
id=MenuStyle_Grid
Version=4,10,0,0
Category=C4D_StaticBack
Width=1
Height=1
Offset=-1,-1

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

View File

@ -0,0 +1,54 @@
/**
Grid
Shows a simple grid menu.
*/
#include MenuStyle_List
local Name = "Grid Menu";
func Construction()
{
inherited(...);
this.Style = GUI_GridLayout;
}
// custom_menu_id should be passed if the menu was manually opened and not via Open()
func AddItem(symbol, string text, user_ID, proplist target, command, parameter, custom_entry, custom_menu_id)
{
custom_menu_id = custom_menu_id ?? menu_id;
var on_hover = GuiAction_SetTag(nil, 0, "OnHover");
if (on_mouse_over_callback)
on_hover = [on_hover, GuiAction_Call(this, "DoCallback", on_mouse_over_callback)];
var on_hover_stop = GuiAction_SetTag(nil, 0, "Std");
if (on_mouse_out_callback)
on_hover_stop = [on_hover_stop, GuiAction_Call(this, "DoCallback", on_mouse_out_callback)];
var ID = GetLength(entries) + 1;
if (!custom_entry)
{
custom_entry = {Hgt = [0, 64], Wdt = [0, 64], desc = {Y = [1000, -15]}};
custom_entry.Symbol = symbol;
custom_entry.desc.Text = text;
custom_entry.desc.Style = GUI_TextRight;
custom_entry.ID = ID;
custom_entry.Target = this;
custom_entry.Priority = ID;
custom_entry.BackgroundColor = {Std = 0, OnHover = 0x50ff0000};
custom_entry.OnClick = GuiAction_Call(this, "OnClick");
custom_entry.OnMouseIn = on_hover;
custom_entry.OnMouseOut = on_hover_stop;
}
entries[ID - 1] = [target, command, parameter, user_ID];
this[Format("menuChild%d", ID)] = custom_entry;
// need to add to existing menu?
if (custom_menu_id)
{
var temp = {child = custom_entry};
CustomGuiUpdate(temp, custom_menu_id, this.ID, this);
}
return custom_entry;
}

View File

@ -0,0 +1,2 @@
Name=Gitter
Description=Ein einfaches Gittermenü.

View File

@ -0,0 +1,2 @@
Name=List
Description=Shows a simple list menu.

View File

@ -0,0 +1 @@
This folder contains some easy-to-use styles for working with menus.

View File

@ -0,0 +1,7 @@
[DefCore]
id=MenuStyle_List
Version=4,10,0,0
Category=C4D_StaticBack
Width=1
Height=1
Offset=-1,-1

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

View File

@ -0,0 +1,144 @@
/**
List
Shows a simple list menu.
*/
local Name = "List Menu";
local entries;
local on_mouse_over_callback, on_mouse_out_callback;
local on_close_callback;
local permanent;
local menu_id;
func Construction()
{
entries = [];
this.Style = GUI_VerticalLayout;
this.Target = this;
this.ID = 0xffffff;
this.OnClose = GuiAction_Call(this, "OnCloseCallback");
}
func OnCloseCallback()
{
menu_id = 0;
Close();
}
func Close()
{
if (menu_id)
CustomGuiClose(menu_id);
if (on_close_callback && on_close_callback[0])
on_close_callback[0]->Call(on_close_callback[1], on_close_callback[2]);
RemoveObject();
}
func SetPermanent(bool perm) { permanent = perm ?? true; }
func SetCloseCallback(proplist target, callback, parameter)
{
on_close_callback = [target, callback, parameter];
}
func SetMouseOverCallback(proplist target, callback)
{
on_mouse_over_callback = [target, callback];
}
func SetMouseOutCallback(proplist target, callback)
{
on_mouse_out_callback = [target, callback];
}
// custom_menu_id should be passed if the menu was manually opened and not via Open()
func AddItem(symbol, string text, user_ID, proplist target, command, parameter, custom_entry, custom_menu_id)
{
custom_menu_id = custom_menu_id ?? menu_id;
var on_hover = GuiAction_SetTag(nil, 0, "OnHover");
if (on_mouse_over_callback)
on_hover = [on_hover, GuiAction_Call(this, "DoCallback", on_mouse_over_callback)];
var on_hover_stop = GuiAction_SetTag(nil, 0, "Std");
if (on_mouse_out_callback)
on_hover_stop = [on_hover_stop, GuiAction_Call(this, "DoCallback", on_mouse_out_callback)];
var ID = GetLength(entries) + 1;
if (!custom_entry)
{
custom_entry = {Hgt = [0, 64], sym = {Wdt = [0, 64], Hgt = [0, 64]}, desc = {X = [0, 64]}};
custom_entry.sym.Symbol = symbol;
custom_entry.desc.Text = text;
custom_entry.desc.Style = GUI_TextVCenter;
custom_entry.Style = GUI_FitChildren;
custom_entry.ID = ID;
custom_entry.Target = this;
custom_entry.Priority = ID;
custom_entry.BackgroundColor = {Std = 0, OnHover = 0x50ff0000};
custom_entry.OnClick = GuiAction_Call(this, "OnClick");
custom_entry.OnMouseIn = on_hover;
custom_entry.OnMouseOut = on_hover_stop;
}
entries[ID - 1] = [target, command, parameter, user_ID];
this[Format("menuChild%d", ID)] = custom_entry;
// need to add to existing menu?
if (custom_menu_id)
{
var temp = {child = custom_entry};
CustomGuiUpdate(temp, custom_menu_id, this.ID, this);
}
return custom_entry;
}
// can be used when the menu has already been opened
// needs to be passed the menu ID if the menu was not opened using Open()
func RemoveItem(user_ID, int custom_menu_id)
{
custom_menu_id = custom_menu_id ?? menu_id;
for (var i = 0; i < GetLength(entries); ++i)
{
var ID = i+1;
if (!entries[i]) continue;
if (entries[i][3] != user_ID) continue;
CustomGuiClose(custom_menu_id, ID, this);
entries[i] = nil;
return true;
}
return false;
}
func DoCall(int ID, command, proplist target, bool noclose, int player)
{
var self = this; // safety
var entry = entries[ID - 1];
target = target ?? entry[0];
// target removed? safety first!
if (target)
{
if (target->Call(command ?? entry[1], entry[2], entry[3], player) == -1) return;
}
if (self)
if (!noclose && !permanent)
Close();
}
func OnClick(int player, int ID, int subwindowID, object target, data)
{
DoCall(subwindowID, nil, nil, nil, player);
}
func DoCallback(int player, int ID, int subwindowID, object target, data)
{
DoCall(subwindowID, data[1], data[0], true, player);
}
func Open()
{
menu_id = CustomGuiOpen(this);
return menu_id;
}

View File

@ -0,0 +1,2 @@
Name=Liste
Description=Ein einfaches Listenmenü.

View File

@ -0,0 +1,2 @@
Name=List
Description=Shows a simple list menu.

View File

@ -0,0 +1,76 @@
/*
This file contains functions that are used for layouting custom menus.
*/
global func CreateCustomMenu(id menuStyle)
{
var menu = CreateObject(menuStyle);
menu->SetPosition(1, 1);
return menu;
}
global func GuiAction_Call(proplist target, string function, value)
{
return [GUI_Call, target, function, value];
}
global func GuiAction_SetTag(object target, int subwindow, string tag)
{
return [GUI_SetTag, target, subwindow, tag];
}
global func Gui_AddMargin(proplist submenu, int marginX, int marginY)
{
submenu.X = submenu.X ?? [0, 0];
submenu.Y = submenu.Y ?? [0, 0];
submenu.Wdt = submenu.Wdt ?? [1000, 0];
submenu.Hgt = submenu.Hgt ?? [1000, 0];
// safety, the coordinates could be a single value
if (GetType(submenu.X) != C4V_Array) submenu.X = [submenu.X, 0];
if (GetType(submenu.Y) != C4V_Array) submenu.Y = [submenu.Y, 0];
if (GetType(submenu.Wdt) != C4V_Array) submenu.Wdt = [submenu.Wdt, 0];
if (GetType(submenu.Hgt) != C4V_Array) submenu.Hgt = [submenu.Hgt, 0];
submenu.X[1] += marginX;
submenu.Y[1] += marginY;
submenu.Wdt[1] -= marginX;
submenu.Hgt[1] -= marginY;
return true;
}
global func Gui_AddCloseButton(proplist menu, proplist target, string callback, parameter)
{
var close_button =
{
Priority = 0x0fffff,
X = [1000, -32], Y = 0,
Wdt = 1000, Hgt = [0, 32],
Symbol = Icon_Cancel,
BackgroundColor = {Std = 0, Hover = 0x50ffff00},
OnMouseIn = GuiAction_SetTag(nil, nil, "Hover"),
OnMouseOut = GuiAction_SetTag(nil, nil, "Std"),
OnClick = GuiAction_Call(target, callback, parameter)
};
Gui_AddSubwindow(close_button, menu);
return close_button;
}
global func Gui_UpdateText(string text, int menu, int submenu, object target)
{
var update = {Text = text};
CustomGuiUpdate(update, menu, submenu, target);
return true;
}
// adds proplist /submenu/ as a new property to /menu/
global func Gui_AddSubwindow(proplist submenu, proplist menu)
{
do
{
var uniqueID = Format("child%d", RandomX(10000, 0xffffff));
if (menu[uniqueID] != nil) continue;
menu[uniqueID] = submenu;
return true;
} while (true);
}

View File

@ -0,0 +1,3 @@
[Scenario]
Title=Menu Test

View File

@ -0,0 +1,391 @@
static active_menu;
func Initialize()
{
var starter_menu =
{
Style = GUI_Multiple | GUI_TextVCenter | GUI_TextHCenter,
Decoration = GUI_MenuDeco,
X = [1000, -100], Y = [0, 50],
Wdt = [1000], Hgt = [0, 100],
Text = "OPEN MENU",
BackgroundColor = {Std = 0, Hover = 0xffff0000},
OnMouseIn = GuiAction_SetTag(nil, nil, "Hover"),
OnMouseOut = GuiAction_SetTag(nil, nil, "Std"),
OnClick = GuiAction_Call(Scenario, "StartMenu")
};
CustomGuiOpen(starter_menu);
}
func CloseCurrentMenu()
{
CustomGuiClose(active_menu);
active_menu = 0;
}
/* -------------------------------- MAIN ----------------------------- */
func MainOnHover(parameter, int ID)
{
Gui_UpdateText(parameter, active_menu, 9999);
}
func StartMenu(plr)
{
if (active_menu)
CustomGuiClose(active_menu);
var main_menu =
{
Decoration = GUI_MenuDeco,
head = {Hgt = [0, 50], Text = "Please choose a test!", Style = GUI_TextHCenter | GUI_TextVCenter, IDs = 0},
body = {Y = [0, 60], right = {ID = 9999, X = 500} },
};
Gui_AddCloseButton(main_menu, Scenario, "CloseCurrentMenu");
var menu = CreateCustomMenu(MenuStyle_List);
main_menu.body.left = menu;
menu.Wdt = 500;
menu->SetMouseOverCallback(Scenario, "MainOnHover");
menu->AddItem(Chest, "Test Multiple Lists (Inventory)", nil, Scenario, "StartMultipleListTest", "Shows multiple list-style menus in one big menu.");
menu->AddItem(Rule_TeamAccount, "Test Client/Host (Scenario Options)", nil, Scenario, "StartScenarioOptionsTest", "Shows how to display a dialogue that behaves differently for players.");
menu->AddItem(Clonk, "Test Multiple Windows (Player List)", nil, Scenario, "StartPlayerListTest", "Shows how to display a permanent info dialogue.");
menu->AddItem(Lorry, "Tests Two Grid Menus (Trade Menu)", nil, Scenario, "StartTransferTest", "Shows how to work with two grid menus.");
menu->AddItem(Sproutberry, "Test HP Bars (HP Bars!)", nil, Scenario, "StartHPBarTest", "HP BARS!!!");
active_menu = CustomGuiOpen(main_menu);
}
/* ------------------------ inventory test ----------------------------- */
static selected_inventory, inv_menus;
func StartMultipleListTest()
{
CustomGuiClose(active_menu);
selected_inventory = [];
inv_menus = [];
// layout: headline and four sections with items
var menu =
{
head = { ID = 999, Hgt = [0, 50], Text = "Inventory: <c ff0000>Empty</c>", Style = GUI_TextHCenter | GUI_TextVCenter, BackgroundColor = 0x55000000},
contents = { Y = [0, 50], X = [0, 20], Wdt = [1000, -20] },
};
Gui_AddCloseButton(menu, Scenario, "CloseCurrentMenu");
var inventory = [[Sword, Axe, Club], [IronBomb, Dynamite, Boompack, Firestone], [Bow, Musket, Javelin], [Shield, Bread, Sproutberry, CookedMushroom]];
var x = [0, [500, 20], 0, [500, 20]], y = [0, 0, [500, 20], [500, 20]], w = [[500, -20], 1000, [500, -20], 1000], h = [[500, -20], [500, -20], 1000, 1000];
for (var i = 0; i < 4; ++i)
{
var inv = inventory[i];
var ID = 9000 + i;
var m = CreateCustomMenu(MenuStyle_List);
m.Decoration = GUI_MenuDeco;
m.X = x[i]; m.Y = y[i];
m.Wdt = w[i]; m.Hgt = h[i];
Gui_AddSubwindow(m, menu.contents);
PushBack(inv_menus, m); // remember for later
for (var obj in inv)
m->AddItem(obj, obj.Description, nil, Scenario, "SelectInventory", [obj, ID]);
}
active_menu = CustomGuiOpen(menu);
}
func SelectInventory(info)
{
var obj = info[0];
var ID = info[1];
PushBack(selected_inventory, obj);
var text = "Your inventory: ";
for (var item in selected_inventory)
text = Format("%s %s,", text, item.Name);
if (GetLength(selected_inventory) == 4)
{
Log("HERO! YOU WILL SPAWN NOW! %s", text);
for (var m in inv_menus)
if (m) m->Close();
CustomGuiClose(active_menu);
}
else
{
var update = { Text = text };
CustomGuiUpdate(update, active_menu, 999);
}
}
/* ------------------------ scenario options test ----------------------------- */
static scenoptions_dummies;
func StartScenarioOptionsTest(parameter, int ID, int player)
{
CustomGuiClose(active_menu);
scenoptions_dummies = [];
scenoptions_dummies[0] = CreateObject(Dummy, nil, nil, player);
scenoptions_dummies[1] = CreateObject(Dummy, nil, nil, player);
for (var i = 0; i <= 1; ++i)
{
if (i == 0)
{
scenoptions_dummies[i]->SetOwner(player);
scenoptions_dummies[i].Visibility = VIS_Owner;
}
else
{
var vis = [VIS_Select];
for (var p = 0; p <= GetPlayerCount(); ++p)
{
var plr = GetPlayerByIndex(p);
if (plr == player) continue;
vis[plr + 1] = 1;
}
scenoptions_dummies[i].Visibility = vis;
}
}
var menu =
{
list =
{
Wdt = 500,
Style = GUI_VerticalLayout,
},
right = {
X = 500,
Decoration = GUI_MenuDeco,
hostdesc =
{
ID = 1,
Target = scenoptions_dummies[0],
Text = "Please select the scenario options!"
},
clientdesc =
{
ID = 1,
Target = scenoptions_dummies[1],
Text = Format("%s can set the options now! Please wait!", GetTaggedPlayerName(player))
}
}
};
Gui_AddMargin(menu.right.hostdesc, 25, 25);
Gui_AddMargin(menu.right.clientdesc, 25, 25);
Gui_AddCloseButton(menu, Scenario, "CloseCurrentMenu");
var def, rules =[], i = 0;
while (def = GetDefinition(i++))
{
if (!(def->GetCategory() & C4D_Rule)) continue;
PushBack(rules, {def = def, ID = i});
}
for (var rule in rules)
{
var subm =
{
ID = rule.ID,
Hgt = [0, 64],
icon = {Priority = 10, Symbol = rule.def, Wdt = [0, 64], Hgt = [0, 64]},
text = {Priority = 10, X = [0, 64], Style = GUI_TextVCenter, Text = rule.def.Name},
selector = // only visible for host
{
Target = scenoptions_dummies[0],
Priority = 1,
BackgroundColor = {Std = 0, Hover = 0x50ff0000, On = 0x2000ff00},
OnMouseIn = {
Std = [GuiAction_Call(Scenario, "ScenOptsUpdateDesc", [rule.def, rule.ID, false]), GuiAction_SetTag(nil, nil, "Hover")],
On = GuiAction_Call(Scenario, "ScenOptsUpdateDesc", [rule.def, rule.ID, true])
},
OnMouseOut = { Hover = GuiAction_SetTag(nil, nil, "Std"), On = nil },
OnClick = {
Hover = [GuiAction_Call(Scenario, "ScenOptsActivate", [rule.def, rule.ID]), GuiAction_SetTag(nil, nil, "On")],
On = [GuiAction_Call(Scenario, "ScenOptsDeactivate", [rule.def, rule.ID]), GuiAction_SetTag(nil, nil, "Hover")],
},
},
tick =
{
X = [1000, -60], Y = [500, -15],
Wdt = [1000, -30], Hgt =[500, 15],
Symbol = {Std = 0, Unticked = 0, Ticked = Icon_Ok}
}
};
Gui_AddSubwindow(subm, menu.list);
}
active_menu = CustomGuiOpen(menu);
}
func ScenOptsActivate(int player, int ID, int subwindowID, object target, data)
{
if (!ObjectCount(Find_ID(data[0])))
CreateObject(data[0]);
CustomGuiSetTag("Ticked", active_menu, data[1], nil);
}
func ScenOptsDeactivate(int player, int ID, int subwindowID, object target, data)
{
RemoveAll(Find_ID(data[0]));
CustomGuiSetTag("Unticked", active_menu, data[1], nil);
}
func ScenOptsUpdateDesc(int player, int ID, int subwindowID, object target, data)
{
var text = "<c ff0000>Do you really want to remove the rule???</c>";
if (!data[2])
text = data[0].Description;
Gui_UpdateText(text, active_menu, 1, scenoptions_dummies[0]);
}
/* ------------------------ player list test ----------------------------- */
static player_list_menu;
func StartPlayerListTest(parameter, int ID, int player)
{
if (player_list_menu)
{
CustomGuiClose(player_list_menu);
player_list_menu = nil;
return -1;
}
var menu =
{
X = [1000, -150], Y = [0, 100],
Wdt = [1000, -5], Hgt = [0, 200],
Style = GUI_Multiple | GUI_VerticalLayout | GUI_FitChildren,
BackgroundColor = 0x30000000,
};
var player_names = [];
for (var i = 0; i < 15; ++i)
{
var p = GetPlayerByIndex(i);
var name;
if (p == NO_OWNER) name = Format("Player %d", i + 1);
else name = GetTaggedPlayerName(p);
var subm =
{
Priority = i,
Hgt = [0, 25],
Text = name,
Style = GUI_TextRight | GUI_TextVCenter,
icon =
{
Symbol = Clonk,
Wdt = [0, 25]
}
};
Gui_AddSubwindow(subm, menu);
}
player_list_menu = CustomGuiOpen(menu);
return -1; // keep open
}
/* ------------------------ transfer test ----------------------------- */
static transfer_left, transfer_right, transfer_menus, transfer_id_count;
func StartTransferTest()
{
CustomGuiClose(active_menu);
if (transfer_left == nil)
{
transfer_left = [Rock, Loam, Wood, Metal, Nugget, Coal, Shovel, Sword, Bow, Arrow, Boompack];
transfer_right = [Clonk];
transfer_menus = [];
transfer_id_count = 1;
}
// layout: headline and two submenus
var menu =
{
head = { Hgt = [0, 50], Text = "Welcome to the trade menu!", Style = GUI_TextHCenter | GUI_TextVCenter},
contents = { Y = [0, 25], X = [0, 20], Wdt = [1000, -20] },
};
Gui_AddCloseButton(menu, Scenario, "CloseCurrentMenu");
for (var i = 0; i < 2; ++i)
{
var m = CreateCustomMenu(MenuStyle_Grid);
m.Decoration = GUI_MenuDeco;
m.Text = "FROM";
m.Style = GUI_TextHCenter | GUI_GridLayout;
if (i == 1)
{
m.X = [500, 15];
m.Text = "TO";
} else m.Wdt = [500, -15];
Gui_AddSubwindow(m, menu.contents);
var a = transfer_left;
if (i == 1) a = transfer_right;
for (var c = 0; c < GetLength(a); ++c)
{
var obj = a[c];
m->AddItem(obj, obj.Name, ++transfer_id_count, Scenario, "SelectTransferGood", [obj, i]);
}
transfer_menus[i] = m;
}
active_menu = CustomGuiOpen(menu);
}
func SelectTransferGood(data, int user_id, int player)
{
var obj = data[0];
var fromLeft = 0 == data[1];
var menu = transfer_menus[data[1]];
// first, move item from array to array
var from = transfer_left, to = transfer_right;
if (!fromLeft)
{
from = transfer_right;
to = transfer_left;
}
var found = false;
for (var i = 0; i < GetLength(from); ++i)
{
if (from[i] != obj) continue;
found = true;
PushBack(to, obj);
RemoveArrayIndex(from, i);
break;
}
if (!found) return -1;
if (!menu->RemoveItem(user_id, active_menu)) Log("remove fail!");
transfer_menus[1 - data[1]]->AddItem(obj, obj.Name, user_id, Scenario, "SelectTransferGood", [obj, 1 - data[1]], nil, active_menu);
return -1;
}
/* ------------------------ HP bar test ----------------------------- */
static HP_bar_menu;
func StartHPBarTest(parameter, int ID, int player)
{
if (HP_bar_menu)
{
CustomGuiClose(HP_bar_menu);
return -1; // keep open
}
var menu =
{
X = [0, 10], Y = [0, 50],
Wdt = [0, 15], Hgt = [1000, -50],
Style = GUI_Multiple | GUI_IgnoreMouse,
BackgroundColor = RGB(255, 0, 0),
blackOverlay = {ID = 1, Hgt = 0, BackgroundColor = RGB(10, 10, 10)},
OnClose = GuiAction_Call(Scenario, "OnHPBarClose")
};
if (!GetEffect("FoolAroundWithHPBars"))
AddEffect("FoolAroundWithHPBar", nil, 1, 2);
HP_bar_menu = CustomGuiOpen(menu);
return -1; // keep open
}
global func FxFoolAroundWithHPBarTimer(target, effect, time)
{
var state = Abs(Cos(time, 1000));
var update = {Hgt = state};
CustomGuiUpdate(update, HP_bar_menu, 1);
}
func OnHPBarClose()
{
RemoveEffect("FoolAroundWithHPBar");
HP_bar_menu = nil;
Log("HP bar off!");
}

View File

@ -49,6 +49,7 @@
#include <C4PlayerList.h>
#include <C4GameObjects.h>
#include <C4GameControl.h>
#include <C4GuiWindow.h>
#ifndef NOAULDEBUG
#include <C4AulDebug.h>
@ -418,6 +419,44 @@ void C4ControlPlayerCommand::CompileFunc(StdCompiler *pComp)
pComp->Value(mkNamingAdapt(mkIntPackAdapt(iAddMode), "AddMode", 0));
C4ControlPacket::CompileFunc(pComp);
}
// *** C4ControlMenuCommand
C4ControlMenuCommand::C4ControlMenuCommand(int32_t actionID, int32_t player, int32_t menuID, int32_t subwindowID, C4Object *target, unsigned int tag, int32_t actionType)
: actionID(actionID), player(player), menuID(menuID), subwindowID(subwindowID), target(target ? target->Number : 0), tag(static_cast<int32_t>(tag)), actionType(actionType)
{
}
void C4ControlMenuCommand::Execute() const
{
// invalid action? The action needs to be in bounds!
if (actionType < 0 || actionType >= C4GuiWindowPropertyName::_lastProp)
{
// this could only come from a malicious attempt to crash the engine!
Log("Warning: invalid action type for C4ControlMenuCommand!");
return;
}
C4GuiWindow *menu = ::GuiWindowRoot.GetChildByID(menuID);
// menu was closed?
if (!menu) return;
C4Object *obj = target ? ::Objects.ObjectPointer(target) : 0;
// target has been removed in the meantime? abort now
if (target && !obj) return;
menu->ExecuteCommand(actionID, player, subwindowID, actionType, obj, static_cast<unsigned int>(tag));
}
void C4ControlMenuCommand::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkNamingAdapt(mkIntPackAdapt(actionID), "ID", -1));
pComp->Value(mkNamingAdapt(mkIntPackAdapt(player), "Player", -1));
pComp->Value(mkNamingAdapt(mkIntPackAdapt(menuID), "Menu", 0));
pComp->Value(mkNamingAdapt(mkIntPackAdapt(subwindowID), "Window", 0));
pComp->Value(mkNamingAdapt(mkIntPackAdapt(actionType), "Action", 0));
pComp->Value(mkNamingAdapt(target, "Target", 0));
C4ControlPacket::CompileFunc(pComp);
}
// *** C4ControlSyncCheck

View File

@ -214,6 +214,19 @@ public:
DECLARE_C4CONTROL_VIRTUALS
};
class C4ControlMenuCommand : public C4ControlPacket // sync
{
public:
C4ControlMenuCommand()
: menuID(0), subwindowID(0) { }
C4ControlMenuCommand(int32_t actionID, int32_t player, int32_t menuID, int32_t subwindowID,
C4Object *target, unsigned int tag, int32_t actionType);
protected:
int32_t actionID, player, menuID, subwindowID, target, tag, actionType;
public:
DECLARE_C4CONTROL_VIRTUALS
};
class C4ControlSyncCheck : public C4ControlPacket // not sync
{
public:

View File

@ -69,6 +69,7 @@
#include <C4RankSystem.h>
#include <C4RoundResults.h>
#include <C4GameMessage.h>
#include <C4GuiWindow.h>
#include <C4Material.h>
#include <C4Network2Reference.h>
#include <C4Weather.h>
@ -653,7 +654,7 @@ void C4Game::Clear()
fPreinited = false;
C4PropListNumbered::ResetEnumerationIndex();
// FIXME: remove this
Default();
}
@ -919,6 +920,7 @@ void C4Game::ClearPointers(C4Object * pObj)
::MessageInput.ClearPointers(pObj);
::Console.ClearPointers(pObj);
::MouseControl.ClearPointers(pObj);
::GuiWindowRoot.ClearPointers(pObj);
TransferZones.ClearPointers(pObj);
if (pGlobalEffects)
pGlobalEffects->ClearPointers(pObj);

View File

@ -126,7 +126,7 @@ void C4GraphicsSystem::Execute()
return;
}
// Reset object audibility
::Objects.ResetAudibility();
@ -182,6 +182,7 @@ void C4GraphicsSystem::Default()
ShowPathfinder=false;
ShowNetstatus=false;
ShowSolidMask=false;
ShowMenuInfo=false;
ShowHelp=false;
FlashMessageText[0]=0;
FlashMessageTime=0; FlashMessageX=FlashMessageY=0;
@ -306,6 +307,7 @@ void C4GraphicsSystem::DeactivateDebugOutput()
ShowPathfinder=false; // allow pathfinder! - why this??
ShowSolidMask=false;
ShowNetstatus=false;
ShowMenuInfo=false;
}
void C4GraphicsSystem::DrawHoldMessages()
@ -398,7 +400,7 @@ void C4GraphicsSystem::DrawHelp()
strText.AppendFormat("\n\n[%s]\n\n", "Debug");
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgModeToggle").getData(), LoadResStr("IDS_CTL_DEBUGMODE"));
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowVtxToggle").getData(), "Entrance+Vertices");
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowActionToggle").getData(), "Actions/Commands/Pathfinder");
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowActionToggle").getData(), "Actions/Commands/Pathfinder/Menu Info");
strText.AppendFormat("<c ffff00>%s</c> - %s\n", GetKeyboardInputName("DbgShowSolidMaskToggle").getData(), "SolidMasks");
pDraw->TextOut(strText.getData(), ::GraphicsResource.FontRegular, 1.0, FullScreen.pSurface,
iX + iWdt/2 + 64, iY + 64, C4Draw::DEFAULT_MESSAGE_COLOR, ALeft);
@ -422,14 +424,16 @@ bool C4GraphicsSystem::ToggleShowVertices()
bool C4GraphicsSystem::ToggleShowAction()
{
if (!Game.DebugMode && !Console.Active) { FlashMessage(LoadResStr("IDS_MSG_NODEBUGMODE")); return false; }
if (!(ShowAction || ShowCommand || ShowPathfinder))
if (!(ShowAction || ShowCommand || ShowPathfinder || ShowMenuInfo))
{ ShowAction = true; FlashMessage("Actions"); }
else if (ShowAction)
{ ShowAction = false; ShowCommand = true; FlashMessage("Commands"); }
else if (ShowCommand)
{ ShowCommand = false; ShowPathfinder = true; FlashMessage("Pathfinder"); }
else if (ShowPathfinder)
{ ShowPathfinder = false; FlashMessageOnOff("Actions/Commands/Pathfinder", false); }
{ ShowPathfinder = false; ShowMenuInfo = true; FlashMessage("Menu Info"); }
else if (ShowMenuInfo)
{ ShowMenuInfo = false; FlashMessageOnOff("Actions/Commands/Pathfinder", false); }
return true;
}

View File

@ -45,6 +45,7 @@ public:
bool ShowPathfinder;
bool ShowNetstatus;
bool ShowSolidMask;
bool ShowMenuInfo;
C4Video Video;
C4LoaderScreen *pLoaderScreen;
void Default();

View File

@ -39,6 +39,7 @@
#include <C4MouseControl.h>
#include <C4PXS.h>
#include <C4GameMessage.h>
#include <C4GuiWindow.h>
#include <C4GraphicsResource.h>
#include <C4GraphicsSystem.h>
#include <C4Landscape.h>
@ -265,7 +266,7 @@ void C4Viewport::Draw(C4TargetFacet &cgo0, bool fDrawOverlay)
// Draw overlay
if (!Game.C4S.Head.Film || !Game.C4S.Head.Replay) Game.DrawCursors(cgo, Player);
/* Fog of war disabled, see above
/* Fog of war disabled, see above
// FogOfWar-mod off
pDraw->SetClrModMapEnabled(false);
@ -295,14 +296,20 @@ void C4Viewport::Draw(C4TargetFacet &cgo0, bool fDrawOverlay)
if (Application.isEditor) Console.EditCursor.Draw(cgo);
DrawOverlay(gui_cgo, GameZoom);
// Game messages
C4ST_STARTNEW(MsgStat, "C4Viewport::DrawOverlay: Messages")
pDraw->SetZoom(0, 0, 1.0);
::Messages.Draw(gui_cgo, cgo, Player);
C4ST_STOP(MsgStat)
// ingame menus
C4ST_STARTNEW(GuiWindowStat, "C4Viewport::DrawOverlay: Menus")
pDraw->SetZoom(0, 0, 1.0);
::GuiWindowRoot.Draw(gui_cgo, Player);
C4ST_STOP(GuiWindowStat)
DrawOverlay(gui_cgo, GameZoom);
// Netstats
if (::GraphicsSystem.ShowNetstatus)
::Network.DrawStatus(gui_cgo);
@ -470,7 +477,7 @@ void C4Viewport::AdjustPosition()
// View position
if (PlayerLock && ValidPlr(Player))
{
float ScrollRange = Min(ViewWdt/(10*Zoom),ViewHgt/(10*Zoom));
float ExtraBoundsX = 0, ExtraBoundsY = 0;
if (pPlr->ViewMode == C4PVM_Scrolling)
@ -585,7 +592,7 @@ void C4Viewport::DrawPlayerInfo(C4TargetFacet &cgo)
{
C4Facet ccgo;
if (!ValidPlr(Player)) return;
// Controls
DrawPlayerStartup(cgo);
}

View File

@ -43,6 +43,7 @@
#include <C4GraphicsSystem.h>
#include <C4Log.h>
#include <C4MessageInput.h>
#include <C4GuiWindow.h>
#include <C4MouseControl.h>
#include <C4ObjectInfoList.h>
#include <C4Player.h>
@ -2129,6 +2130,67 @@ static bool FnCustomMessage(C4PropList * _this, C4String *pMsg, C4Object *pObj,
return ::Messages.New(iType,sMsg,pObj,iOwner,iOffX,iOffY,(uint32_t)dwClr, idDeco, pSrc, dwFlags, iHSize);
}
static int FnCustomGuiOpen(C4PropList * _this, C4PropList *menu)
{
C4GuiWindow *window = new C4GuiWindow;
::GuiWindowRoot.AddChild(window);
if (!window->CreateFromPropList(menu, true))
{
::GuiWindowRoot.RemoveChild(window, false);
return 0;
}
return window->GetID();
}
static bool FnCustomGuiSetTag(C4PropList * _this, C4String *tag, int32_t menuID, int32_t childID, C4Object *target)
{
C4GuiWindow *window = ::GuiWindowRoot.GetChildByID(menuID);
if (!window) return false;
if (childID) // note: valid child IDs are always non-zero
{
C4GuiWindow *subwindow = window->GetSubWindow(childID, target);
if (!subwindow) return false;
subwindow->SetTag(tag);
return true;
}
window->SetTag(tag);
return true;
}
static bool FnCustomGuiClose(C4PropList *_this, int32_t menuID, int32_t childID, C4Object *target)
{
C4GuiWindow *window = ::GuiWindowRoot.GetChildByID(menuID);
if (!window) return false;
if (childID) // note: valid child IDs are always non-zero
{
C4GuiWindow *subwindow = window->GetSubWindow(childID, target);
if (!subwindow) return false;
subwindow->Close();
return true;
}
window->Close();
return true;
}
static bool FxCustomGuiUpdate(C4PropList *_this, C4PropList *update, int32_t menuID, int32_t childID, C4Object *target)
{
if (!update) return false;
C4GuiWindow *window = ::GuiWindowRoot.GetChildByID(menuID);
if (!window) return false;
if (childID) // note: valid child IDs are always non-zero
{
C4GuiWindow *subwindow = window->GetSubWindow(childID, target);
if (!subwindow) return false;
subwindow->CreateFromPropList(update, false, true);
return true;
}
window->CreateFromPropList(update, false, true);
return true;
}
/*static long FnSetSaturation(C4AulContext *ctx, long s)
{
return pDraw->SetSaturation(BoundBy(s,0l,255l));
@ -2423,6 +2485,10 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "ExtractMaterialAmount", FnExtractMaterialAmount);
AddFunc(pEngine, "GetEffectCount", FnGetEffectCount);
AddFunc(pEngine, "CustomMessage", FnCustomMessage);
AddFunc(pEngine, "CustomGuiOpen", FnCustomGuiOpen);
AddFunc(pEngine, "CustomGuiSetTag", FnCustomGuiSetTag);
AddFunc(pEngine, "CustomGuiClose", FnCustomGuiClose);
AddFunc(pEngine, "CustomGuiUpdate", FxCustomGuiUpdate);
AddFunc(pEngine, "PauseGame", FnPauseGame, false);
AddFunc(pEngine, "PathFree", FnPathFree);
AddFunc(pEngine, "PathFree2", FnPathFree2);
@ -2578,6 +2644,19 @@ C4ScriptConstDef C4ScriptGameConstMap[]=
{ "PLRZOOM_LimitMin" ,C4V_Int, PLRZOOM_LimitMin },
{ "PLRZOOM_LimitMax" ,C4V_Int, PLRZOOM_LimitMax },
{ "GUI_SetTag" ,C4V_Int, C4GuiWindowActionID::SetTag },
{ "GUI_Call" ,C4V_Int, C4GuiWindowActionID::Call },
{ "GUI_GridLayout" ,C4V_Int, C4GuiWindowStyleFlag::GridLayout },
{ "GUI_VerticalLayout" ,C4V_Int, C4GuiWindowStyleFlag::VerticalLayout },
{ "GUI_TextVCenter" ,C4V_Int, C4GuiWindowStyleFlag::TextVCenter },
{ "GUI_TextHCenter" ,C4V_Int, C4GuiWindowStyleFlag::TextHCenter },
{ "GUI_TextRight" ,C4V_Int, C4GuiWindowStyleFlag::TextRight },
{ "GUI_TextBottom" ,C4V_Int, C4GuiWindowStyleFlag::TextBottom },
{ "GUI_TextTop" ,C4V_Int, C4GuiWindowStyleFlag::None }, // note that top and left are considered default
{ "GUI_TextLeft" ,C4V_Int, C4GuiWindowStyleFlag::None }, // they are only included for completeness
{ "GUI_FitChildren" ,C4V_Int, C4GuiWindowStyleFlag::FitChildren },
{ "GUI_Multiple" ,C4V_Int, C4GuiWindowStyleFlag::Multiple },
{ "GUI_IgnoreMouse" ,C4V_Int, C4GuiWindowStyleFlag::IgnoreMouse },
{ NULL, C4V_Nil, 0}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,298 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2013 David Dormagen
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
/* a flexisble ingame menu system that can be composed out of multiple windows */
#ifndef INC_C4GuiWindow
#define INC_C4GuiWindow
#include <C4Surface.h>
#include <C4Gui.h>
#include <C4Value.h>
#include <map>
enum C4GuiWindowPropertyName
{
left = 0,
top,
right,
bottom,
relLeft,
relRight,
relTop,
relBottom,
backgroundColor,
frameDecoration,
symbolObject,
symbolDef,
text,
onClickAction,
onMouseInAction,
onMouseOutAction,
onCloseAction,
style,
priority,
_lastProp
};
enum C4GuiWindowActionID
{
SetTag = 1,
Call,
};
enum C4GuiWindowStyleFlag
{
None = 0,
GridLayout = 1,
VerticalLayout = 2,
TextVCenter = 4,
TextHCenter = 8,
TextRight = 16,
TextBottom = 32,
FitChildren = 64,
Multiple = 128,
IgnoreMouse = 256,
};
class C4GuiWindow;
class C4GuiWindowAction
{
friend class C4GuiWindow;
private:
// the ID is unique among all actions. It is used later to synchronize callbacks
int32_t id;
int32_t action;
C4GuiWindowAction *nextAction; // a linked list of actions
// note: depending on the action not all of the following attributes always have values
C4PropList *target; // contains a valid C4Object in case of SetTag, a generic proplist in case of Call
C4String *text; // can be either a function name to call or a tag to set
C4Value value; // arbitrary value used for Call
int32_t subwindowID;
public:
C4GuiWindowAction() : id(0), action(0), nextAction(0), target(0), text(0), value(0), subwindowID(0) { }
~C4GuiWindowAction();
void ClearPointers(C4Object *pObj);
bool Init(C4ValueArray *array, int32_t index = 0); // index is the current action in an array of actions
// executes non-synced actions and syncs the others
// the tag and action type parameters are only used to be able to sync commands
void Execute(C4GuiWindow *parent, int32_t player, unsigned int tag, int32_t actionType);
// used to execute synced commands, explanation see C4GuiWindow::ExecuteCommand
bool ExecuteCommand(int32_t actionID, C4GuiWindow *parent, int32_t player);
// used for serialization. The "first" parameter is used so that chained actions are stored correctly into an array
const C4Value ToC4Value(bool first = true);
};
class C4GuiWindowProperty
{
friend class C4GuiWindow;
private:
typedef union
{
void *data;
float f;
int32_t d;
C4Object *obj;
C4Def *def;
C4GUI::FrameDecoration *deco;
StdCopyStrBuf *strBuf;
C4GuiWindowAction *action;
} Prop;
Prop *current;
// the last tag is used to be able to call the correct action on re-synchronizing commands
unsigned int currentTag;
std::map<unsigned int, Prop> taggedProperties;
void CleanUp(Prop &prop);
void CleanUpAll();
int32_t type; // which property do I stand for?
void SetInt(unsigned int hash, int32_t to) { taggedProperties[hash] = Prop(); current = &taggedProperties[hash]; current->d = to; }
void SetFloat(unsigned int hash, float to) { taggedProperties[hash] = Prop(); current = &taggedProperties[hash]; current->f = to; }
void SetNull(unsigned int hash) { taggedProperties[hash] = Prop(); current = &taggedProperties[hash]; current->data = 0; }
public:
~C4GuiWindowProperty();
C4GuiWindowProperty() : current(0), currentTag(0), type(-1) {}
void Set(const C4Value &value, unsigned int hash);
int32_t GetInt() { return current->d; }
float GetFloat() { return current->f; }
C4Object *GetObject() { return current->obj; }
C4Def *GetDef() { return current->def; }
C4GUI::FrameDecoration *GetFrameDecoration() { return current->deco; }
StdCopyStrBuf *GetStrBuf() { return current->strBuf; }
C4GuiWindowAction *GetAction() { return current->action; }
C4GuiWindowAction *GetActionForTag(unsigned int hash); // used to synchronize actions
bool SwitchTag(C4String *tag);
unsigned int GetCurrentTag() { return currentTag; }
const C4Value ToC4Value();
void ClearPointers(C4Object *pObj);
};
class C4GuiWindowScrollBar
{
friend class C4GuiWindow;
public:
float offset;
C4GuiWindowScrollBar();
virtual ~C4GuiWindowScrollBar();
void ScrollBy(float val) { offset += val; if (offset < 0.0f) offset = 0.0f; else if (offset > 1.0f) offset = 1.0f; }
void Draw(C4TargetFacet &cgo, int32_t player, float parentLeft, float parentTop, float parentRight, float parentBottom);
virtual bool MouseInput(int32_t button, int32_t mouseX, int32_t mouseY, DWORD dwKeyParam);
private:
C4GUI::ScrollBarFacets *decoration;
C4GuiWindow *parent;
};
class C4GuiWindow
{
friend class C4GuiWindowAction;
private:
// the menu ID is always unique, however the sub-menu IDs do NOT have to be unique
// they can be set from script and in combination with the target should suffice to identify windows
int32_t id;
// this is not only a window inside a menu but a top-level-window?
// this does not mean the ::WindowMenuRoot but rather a player-created submenu
bool isMainWindow;
std::list<C4GuiWindow*> children;
C4GuiWindow *parent;
bool wasRemoved; // to notify the window that it should not inform its parent on Close() a second time
bool closeActionWasExecuted; // to prevent a window from calling the close-callback twice even if f.e. closed in the close-callback..
bool visible;
C4Object *target;
const C4Object *GetTarget() { return target; }
C4GuiWindowScrollBar *scrollBar;
// this remembers whether the window currently has mouse focus
// all windows with this property set are remembered by their parents and notified when the mouse left
bool hasMouseFocus; // this needs to be saved in savegames!!!
// OnMouseOut() called by this window, sets "hasMouseFocus" from true to false
// must notify children, too!
void OnMouseOut(int32_t player);
void OnMouseIn(int32_t player); // called by this window, sets "hasMouseFocus" from false to true
// properties are stored extra to make "tags" possible
C4GuiWindowProperty props[C4GuiWindowPropertyName::_lastProp];
void Init();
// withMultipleFlag is there to draw only the non-multiple or the multiple windows
// withMultipleFlag == -1: all windows are drawn (standard)
// withMultipleFlag == 0: only one non-Multiple window is drawn
// withMultipleFlag == 1: only Multiple windows are drawn
// returns whether at least one child was drawn
bool DrawChildren(C4TargetFacet &cgo, int32_t player, float parentLeft, float parentTop, float parentRight, float parentBottom, int32_t withMultipleFlag = -1);
// ID is set by parent, parent gives unique IDs to children
void SetID(int32_t to) { id = to; }
// to be used to generate the quick-access children map for main menus
void ChildGotID(C4GuiWindow *child);
void ChildWithIDRemoved(C4GuiWindow *child);
std::multimap<int32_t, C4GuiWindow *> childrenIDMap;
// should be called when the Priority property of a child changes
// will sort the child correctly into the children list
void ChildChangedPriority(C4GuiWindow *child);
// helper function
// sets property value from possible(!) array
void SetArrayTupleProperty(const C4Value &property, C4GuiWindowPropertyName first, C4GuiWindowPropertyName second, unsigned int hash);
// this is only supposed to be called at ::GuiWindowRoot since it uses the "ID" property
// this is done to make saving easier. Since IDs do not need to be sequential, action&menu IDs can both be derived from "id"
int32_t GenerateMenuID() { return ++id; }
int32_t GenerateActionID() { return ++id; }
void UpdateLayout();
void UpdateLayoutGrid();
void UpdateLayoutVertical();
// children height should be set when enabling a scroll bar so that, with style FitChildren, the size can simply be changed
void EnableScrollBar(bool enable = true, float childrenHeight = 0.0f);
public:
// used by mouse input, this is in screen coordinates
struct _lastDrawPosition
{
float left, right;
float top, bottom;
float topMostChild, bottomMostChild;
int32_t dirty; // indicates wish to update topMostChild and bottomMostChild asap
_lastDrawPosition() : left(0.0f), right(0.0f), top(0.0f), bottom(0.0f), topMostChild(0.0f), bottomMostChild(0.0f), dirty(2) { }
} lastDrawPosition;
bool IsVisible() { return visible; }
void SetVisible(bool f) { visible = f; }
void SetTag(C4String *tag);
C4GuiWindow();
C4GuiWindow(float stdBorderX, float stdBorderY);
virtual ~C4GuiWindow();
int32_t GetID() { return id; }
// finds a child with a certain ID, usually called on ::MainWindowRoot to get submenus
C4GuiWindow *GetChildByID(int32_t child);
// finds any fitting sub menu - not necessarily direct child
// has to be called on children of ::MainWindowRoot, uses the childrenIDMap
// note: always checks the target to avoid ambiguities, even if 0
C4GuiWindow *GetSubWindow(int32_t childID, C4Object *childTarget);
// pass a proplist to create a window + subwindows as specified
// you can call this function on a window more than once
// if isUpdate is true, all new children will have resetStdTag set
bool CreateFromPropList(C4PropList *proplist, bool resetStdTag = false, bool isUpdate = false);
// constructs a C4Value (proplist) that contains everything that is needed for saving this window
const C4Value ToC4Value();
// C4GuiWindow will delete its children on close. Make sure you don't delete anything twice
C4GuiWindow *AddChild(C4GuiWindow *child);
C4GuiWindow *AddChild() { return AddChild(new C4GuiWindow()); }
void ClearChildren(bool close = true); // close: whether to properly "Close" them, alias for RemoveChild
void RemoveChild(C4GuiWindow *child, bool close = true, bool all = false); // child = 0 & all = true to clear all
void Close();
void ClearPointers(C4Object *pObj);
// Draw without parameters can be used for the root
bool Draw(C4TargetFacet &cgo, int32_t player);
bool Draw(C4TargetFacet &cgo, int32_t player, float parentLeft, float parentTop, float parentRight, float parentBottom);
bool GetClippingRect(float &left, float &top, float &right, float &bottom);
// used for commands that have been synchronized and are coming from the command queue
// attention: calls to this need to be synchronized!
bool ExecuteCommand(int32_t actionID, int32_t player, int32_t subwindowID, int32_t actionType, C4Object *target, unsigned int tag);
virtual bool MouseInput(int32_t player, int32_t button, int32_t mouseX, int32_t mouseY, DWORD dwKeyParam);
};
extern C4GuiWindow GuiWindowRoot;
#endif

View File

@ -42,6 +42,7 @@
#include <C4PlayerList.h>
#include <C4GameObjects.h>
#include <C4GameControl.h>
#include <C4GuiWindow.h>
const int32_t C4MC_Drag_None = 0,
C4MC_Drag_Script = 6,
@ -322,20 +323,27 @@ void C4MouseControl::Move(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyFl
case C4MC_Button_Wheel: Wheel(dwKeyFlags); break;
}
// script handling of mouse control for everything but regular movement (which is sent at control frame intervals only)
if (iButton != C4MC_Button_None)
// not if blocked by selection object
if (!TargetObject)
// safety (can't really happen in !IsPassive, but w/e
if (pPlayer && pPlayer->ControlSet)
{
if (pPlayer->ControlSet->IsMouseControlAssigned(iButton))
// are custom menus active?
bool menuProcessed = false;
if (pPlayer)
menuProcessed = ::GuiWindowRoot.MouseInput(Player, iButton, iX, iY, dwKeyFlags);
// if not caught by a menu
if (!menuProcessed)
// script handling of mouse control for everything but regular movement (which is sent at control frame intervals only)
if (iButton != C4MC_Button_None)
// not if blocked by selection object
if (!TargetObject)
// safety (can't really happen in !IsPassive, but w/e
if (pPlayer && pPlayer->ControlSet)
{
int wheel_dir = 0;
if (iButton == C4MC_Button_Wheel) wheel_dir = (short)(dwKeyFlags >> 16);
pPlayer->Control.DoMouseInput(0 /* only 1 mouse supported so far */, iButton, GameX, GameY, GuiX, GuiY, (dwKeyFlags & MK_CONTROL) != 0, (dwKeyFlags & MK_SHIFT) != 0, (dwKeyFlags & MK_ALT) != 0, wheel_dir);
if (!menuProcessed && pPlayer->ControlSet->IsMouseControlAssigned(iButton))
{
int wheel_dir = 0;
if (iButton == C4MC_Button_Wheel) wheel_dir = (short)(dwKeyFlags >> 16);
pPlayer->Control.DoMouseInput(0 /* only 1 mouse supported so far */, iButton, GameX, GameY, GuiX, GuiY, (dwKeyFlags & MK_CONTROL) != 0, (dwKeyFlags & MK_SHIFT) != 0, (dwKeyFlags & MK_ALT) != 0, wheel_dir);
}
}
}
}
void C4MouseControl::DoMoveInput()
@ -441,7 +449,7 @@ void C4MouseControl::Draw(C4TargetFacet &cgo, const ZoomData &GameZoom)
uint32_t BlitMode = DragImageObject->BlitMode;
DragImageObject->ColorMod = (Drag == C4MC_Drag_Script) ? 0x7fffffff : (/*DragImagePhase*/0 ? 0x8f7f0000 : 0x1f007f00);
DragImageObject->BlitMode = C4GFXBLIT_MOD2;
DragImageObject->DrawPicture(ccgo, false, NULL);
DragImageObject->ColorMod = ColorMod;

View File

@ -142,6 +142,7 @@ protected:
public:
bool IsDragging();
bool IsLeftDown() { return LeftButtonDown; }
int32_t GetPlayer() { return Player; }
};

View File

@ -123,6 +123,7 @@ const C4PktHandlingData PktHandlingData[] =
{ CID_PlrControl, PC_Control, "Player Control", false, true, 0, PKT_UNPACK(C4ControlPlayerControl)},
{ CID_PlrCommand, PC_Control, "Player Command", false, true, 0, PKT_UNPACK(C4ControlPlayerCommand)},
{ CID_Message, PC_Control, "Message", false, true, 0, PKT_UNPACK(C4ControlMessage) },
{ CID_MenuCommand, PC_Control, "Menu Command", false, true, 0, PKT_UNPACK(C4ControlMenuCommand)},
{ CID_EMMoveObj, PC_Control, "EM Move Obj", false, true, 0, PKT_UNPACK(C4ControlEMMoveObject)},
{ CID_EMDrawTool, PC_Control, "EM Draw Tool", false, true, 0, PKT_UNPACK(C4ControlEMDrawTool) },

View File

@ -166,7 +166,8 @@ enum C4PacketType
CID_EMMoveObj = CID_First | 0x30,
CID_EMDrawTool = CID_First | 0x31,
CID_DebugRec = CID_First | 0x40
CID_DebugRec = CID_First | 0x40,
CID_MenuCommand = CID_First | 0x41,
};
// packet classes

View File

@ -152,6 +152,18 @@ C4StringTable::C4StringTable()
P[P_Global] = "Global";
P[P_Scenario] = "Scenario";
P[P_JumpSpeed] = "JumpSpeed";
P[P_BackgroundColor] = "BackgroundColor";
P[P_Decoration] = "Decoration";
P[P_Symbol] = "Symbol";
P[P_Target] = "Target";
P[P_Std] = "Std";
P[P_Text] = "Text";
P[P_OnClick] = "OnClick";
P[P_OnMouseIn] = "OnMouseIn";
P[P_OnMouseOut] = "OnMouseOut";
P[P_OnClose] = "OnClose";
P[P_ID] = "ID";
P[P_Style] = "Style";
P[P_Algo] = "Algo";
P[P_Layer] = "Layer";
P[P_Seed] = "Seed";

View File

@ -356,6 +356,18 @@ enum C4PropertyName
P_Global,
P_Scenario,
P_JumpSpeed,
P_BackgroundColor,
P_Decoration,
P_Symbol,
P_Target,
P_Std,
P_Text,
P_ID,
P_OnClick,
P_OnMouseIn,
P_OnMouseOut,
P_OnClose,
P_Style,
P_Algo,
P_Layer,
P_Seed,