Display iPhone OS Keyboard - Solved
by Michael Perry · in iTorque 2D · 02/22/2009 (12:08 pm) · 60 replies
Hey everyone. So, while working with Ecliptic to get the camera/screenshot functionality of an iPhone into iTGB, I had to figure out how to send a message from iTGB to the iPhone SDK.
This is opposite of how input works, as the iPhone SDK detects a touch, creates a TGB mouse event, and sends that to the TGB system.
Fortunately, I have an iTGE demo that does just that! When the user clicks on a text edit control, the iPhone OS keyboard pops up. I had to start digging through the code, and found wrapper that performs this functionality.
This is opposite of how input works, as the iPhone SDK detects a touch, creates a TGB mouse event, and sends that to the TGB system.
Fortunately, I have an iTGE demo that does just that! When the user clicks on a text edit control, the iPhone OS keyboard pops up. I had to start digging through the code, and found wrapper that performs this functionality.
About the author
Programmer.
#2
When a user clicks on a GuiTextEditCtrl in a game, this engine function is called:
Stock Function:
This is what iTGE has:
New code:
PAY DIRT! =)
Let's break that new code chunk down:
OK. So what is this new TextEntry and it's handy getUserText() function?
Next Post...
02/22/2009 (12:10 pm)
I wanted to check the source code of guiTextEditCtrl in TGE 1.5.2 and iTGB. iTGE has the code that loads the iPhone keyboard, but it also uses some custom classes that the other two engines to not support.When a user clicks on a GuiTextEditCtrl in a game, this engine function is called:
Stock Function:
void GuiTextEditCtrl::onMouseUp(const GuiEvent &event)
{
event;
mDragHit = false;
mScrollDir = 0;
mouseUnlock();
}This is what iTGE has:
New code:
void GuiTextEditCtrl::onMouseUp(const GuiEvent &event)
{
event;
mDragHit = false;
mScrollDir = 0;
#ifdef __IPHONE__
// Get text input from the user. This call blocks until the user is finished entering text.
if (TextEntry::getUserText(mTextBuffer))
{
Parent::setText(mTextBuffer.getPtr8());
mCursorPos = mTextBuffer.length();
}
#endif
mouseUnlock();
}PAY DIRT! =)
Let's break that new code chunk down:
// Not a stock function or wrapper
if (TextEntry::getUserText(mTextBuffer))
{
// Stock Torque code, sets the text in control to
// what was grabbed by getUserText()
Parent::setText(mTextBuffer.getPtr8());
// Puts mouse cursor at end of new text
mCursorPos = mTextBuffer.length();
}OK. So what is this new TextEntry and it's handy getUserText() function?
Next Post...
#3
TextEntry.h
TextEntry.mm
Awesome! So that's the Obj-C code that will display the keyboard. However, it appears we have a new @interface to implement. There is apparently a controller for the text entry, the actual hook.
Next post...
02/22/2009 (12:11 pm)
There is a new folder in the Xcode solution called wrappers, which contains two files: TextEntry.h and TextEntry.mm.TextEntry.h
#ifndef TEXTENTRY_INCLUDED
#define TEXTENTRY_INCLUDED
#include <core/stringBuffer.h>
namespace TextEntry
{
// Returns false if the user cancels.
// text [in/out]: The contents of the text entry field. The text entry field is initialized
// with the value of the StringBuffer. On exit, if the function returns true, the StringBuffer
// contains the text entered by the user; otherwise, the StringBuffer is unchanged.
bool getUserText(StringBuffer& text);
}
#endifTextEntry.mm
#include "TextEntry.h"
#include "../Classes/TextEntryController.h"
bool TextEntry::getUserText(StringBuffer& text)
{
TextEntryController* controller = [[TextEntryController alloc] initWithNibName: @"TextEntry" bundle: nil];
UIWindow* window = [UIApplication sharedApplication].keyWindow;
[controller loadView];
NSString* nsText = [[NSString alloc] initWithBytesNoCopy: const_cast<UTF16*>(text.getPtr()) length: text.length() * sizeof(UTF16) encoding: NSUTF16LittleEndianStringEncoding freeWhenDone: NO];
controller.textField.text = nsText;
[nsText release];
controller.view.alpha = 0.0f;
[window addSubview: controller.view];
[UIView beginAnimations: nil context: nil];
controller.view.alpha = 1.0f;
[UIView commitAnimations];
[controller.textField becomeFirstResponder];
while (!controller.finished) {
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
}
[controller.view removeFromSuperview];
bool userCanceled = controller.userCanceled;
if (!userCanceled) {
text.set(reinterpret_cast<const UTF16*>([controller.textField.text cStringUsingEncoding: NSUTF16LittleEndianStringEncoding]));
}
[controller release];
return !userCanceled;
}Awesome! So that's the Obj-C code that will display the keyboard. However, it appears we have a new @interface to implement. There is apparently a controller for the text entry, the actual hook.
Next post...
#4
TextEntryController.h:
TextEntryController.mm:
02/22/2009 (12:13 pm)
This appears to be the final part of this resource:TextEntryController.h:
#import <UIKit/UIKit.h>
@interface TextEntryController : UIViewController
{
UITextField* textField;
bool finished;
bool userCanceled;
}
@property(nonatomic, readonly) IBOutlet UITextField* textField;
@property(nonatomic, readonly) bool finished;
@property(nonatomic, readonly) bool userCanceled;
- (IBAction)onCancelClicked: (id)sender;
- (IBAction)onCommitClicked: (id)sender;
@endTextEntryController.mm:
#import "TextEntryController.h"
@implementation TextEntryController
@synthesize textField;
@synthesize finished;
@synthesize userCanceled;
- (void)dealloc
{
[textField release];
[super dealloc];
}
- (IBAction)onCancelClicked: (id)sender
{
userCanceled = true;
finished = true;
}
- (IBAction)onCommitClicked: (id)sender
{
finished = true;
}
@end
#5
This is called automatically in a TGB, C++ class: GuiTextEditCtrl. Now, what if you want to display the keyboard manually and use the string for something else?
Nice! Now we have a ConsoleFunction we can call in TorqueScript:
02/22/2009 (12:26 pm)
As far as I can tell, this is all you need to get the iPhone OS keyboard to display. This particular approach is performed by calling the C++ method getUserText(...).This is called automatically in a TGB, C++ class: GuiTextEditCtrl. Now, what if you want to display the keyboard manually and use the string for something else?
ConsoleFunction(getiPhoneKeyboardInput, const char *, 1, 1, "getiPhoneKeyboardInput() "
"Returns a string of text from the iPhone OS keyboard")
{
argc;
StringBuffer mTextBuffer;
TextEntry::getUserText(mTextBuffer);
char *ret = Con::getReturnBuffer(dStrlen(mTextBuffer)+1);
return ret;
}Nice! Now we have a ConsoleFunction we can call in TorqueScript:
function setPlayerName()
{
$playerName = getiPhoneKeyboardInput();
}
#6
If this works, getting the camera and other device features should be a lot easier.
Again, all credit goes to the iTGE devs who wrote this code. I have only found the code and created a stand-alone ConsoleFunction.
*END*
02/22/2009 (12:28 pm)
Now, this is all research and was coded by the iTorque devs. I have not ported this to iTGB and tested it. I have to take a break, but if someone implements this before I do, please post success or errors.If this works, getting the camera and other device features should be a lot easier.
Again, all credit goes to the iTGE devs who wrote this code. I have only found the code and created a stand-alone ConsoleFunction.
*END*
#7
I had to remove the } after the first return and add a semicolon after the TextEntry::getUserText(mTextBuffer)..
Now for some reason I am getting this error because of the addition operator.
Any idea whats up? Was I supposed to add this somewhere else? I have tried adding it to the console/consoleFunctions.cc, platformiPhone/iPhoneConsole.mm as well with no luck.
I am going to take a break, will be back soon.
02/22/2009 (3:36 pm)
Everything working so far for me but the Console Function. I added it to the guiTextEditCtrl.cc and had to make some changes to get rid of some compile errors. This is what I got right now but I am getting a strange operator error.//---------------------------------------------
ConsoleFunction(getiPhoneKeyboardInput, const char *, 1, 1, "getiPhoneKeyboardInput() "
"Returns a string of text from the iPhone OS keyboard")
{
argc;
return "";
StringBuffer mTextBuffer;
TextEntry::getUserText(mTextBuffer);
char *ret = Con::getReturnBuffer(mTextBuffer + 1);
return ret;
}
//--------------------------------------------------I had to remove the } after the first return and add a semicolon after the TextEntry::getUserText(mTextBuffer)..
Now for some reason I am getting this error because of the addition operator.
error: no match for 'operator+' in 'mTextBuffer + 1'
Any idea whats up? Was I supposed to add this somewhere else? I have tried adding it to the console/consoleFunctions.cc, platformiPhone/iPhoneConsole.mm as well with no luck.
I am going to take a break, will be back soon.
#8
02/22/2009 (5:20 pm)
Doh, posted bad code:ConsoleFunction(getiPhoneKeyboardInput, const char *, 1, 1, "getiPhoneKeyboardInput() "
"Returns a string of text from the iPhone OS keyboard")
{
argc;
StringBuffer mTextBuffer;
TextEntry::getUserText(mTextBuffer);
char *ret = Con::getReturnBuffer(dStrlen(mTextBuffer)+1);
return ret;
}
#9
All it has is a text edit box, a commit button, and a cancel button.
02/22/2009 (5:28 pm)
I forgot to mention that you need a .xib file that will act as your interface when typing with the keyboard. I have the one that came with iTGE, but it only fits vertical (portrait) resolution. I do not know how to use the Interface Builder, but I would guess the next step is to create a horizontal version of the .xib file I have.All it has is a text edit box, a commit button, and a cancel button.
#10
Thanks for the update.
02/22/2009 (5:31 pm)
Mind posting the .xib file? I will work on creatine a horizontal version of it after I get this to compile.Thanks for the update.
#11
1. Create a directory called "ui" in the same folder as your Xcode project
2. Copy the above file into that directory
3. Add the "ui" directory to the resources group of your Xcode project
02/22/2009 (5:38 pm)
TextEntry.xib file1. Create a directory called "ui" in the same folder as your Xcode project
2. Copy the above file into that directory
3. Add the "ui" directory to the resources group of your Xcode project
#12
I think I will take a break and look at the layout file.
*Edit*
I have created a horizontal version of the layout but I am trying to see how the TextEntryController.h is referencing the xib file so we can have it switch between the horizontal view and the portrait view.
Any ideas?
02/22/2009 (5:49 pm)
Thanks. I think the ConsoleFunction is still missing something. It compiles with this error.error: no matching function for call to 'dStrlen(StringBuffer&)'
I think I will take a break and look at the layout file.
*Edit*
I have created a horizontal version of the layout but I am trying to see how the TextEntryController.h is referencing the xib file so we can have it switch between the horizontal view and the portrait view.
Any ideas?
#13
@property(nonatomic, readonly) IBOutlet UITextField* textField;
- (IBAction)onCancelClicked: (id)sender;
- (IBAction)onCommitClicked: (id)sender;
I'm thinking the interface sends the IDs to the code, or vice versa.
02/22/2009 (6:45 pm)
That's the iPhone SDK code, and I'm not really familiar with it. I see where the hooks are:@property(nonatomic, readonly) IBOutlet UITextField* textField;
- (IBAction)onCancelClicked: (id)sender;
- (IBAction)onCommitClicked: (id)sender;
I'm thinking the interface sends the IDs to the code, or vice versa.
#14
02/22/2009 (6:46 pm)
The forums should be more active tomorrow. I'm out for the night...got a Drop Kick Murphys show to attend! I'll talk to ya some more tomorrow. Post any progress you run into,
#15
Which is very odd since the function is being used several times in the same file!! This is driving me nuts! If any other users successfully do this mod let me know so I can kick myself hehe.
02/22/2009 (7:19 pm)
Well I have started from scratch again and I still get the compile error for the Console function....error: no matching function for call to 'dStrlen(StringBuffer&)
Which is very odd since the function is being used several times in the same file!! This is driving me nuts! If any other users successfully do this mod let me know so I can kick myself hehe.
#16
02/23/2009 (5:57 am)
I think the error is the & operator perhaps? Anyway, good work, keep digging! This would be a lot more useful than my shoddy onscreen GUI keyboard.
#17
02/23/2009 (6:00 am)
Not to mention this is probably a good case study for exposing *more* of the cool iPhone GUI stuff...
#18
02/23/2009 (9:12 am)
I fixed my errors by using the code listed below. I think it is essentially the same but I am not a C++ Guru by any means. Let me know if someone sees a flaw in this.ConsoleFunction(getiPhoneKeyboardInput, const char *, 1, 1, "getiPhoneKeyboardInput() "
"Returns a string of text from the iPhone OS keyboard")
{
argc;
StringBuffer mTextBuffer;
TextEntry::getUserText(mTextBuffer);
char *ret = Con::getReturnBuffer(mTextBuffer.length());
return ret;
}
#19
Thanks guys! We have a keyboard!
02/23/2009 (9:19 am)
I just ran a simple test using the above code and everything worked fine. Keyboad is up and running. Thanks to Michael Perry and the code from the GG Developers!!!Thanks guys! We have a keyboard!
#20
Woo hoo! Nice work Ecliptic =)
It's the start of the work week, so I will not have much time to tackle the camera code. Not to mention I do not have an iPhone to test.
I will take a stab at it here and there, then let you know if I come up with anything. Our work this weekend should make it a lot easier at least.
02/23/2009 (9:48 am)
Wow...what a show last night.Woo hoo! Nice work Ecliptic =)
It's the start of the work week, so I will not have much time to tackle the camera code. Not to mention I do not have an iPhone to test.
I will take a stab at it here and there, then let you know if I come up with anything. Our work this weekend should make it a lot easier at least.
Community Manager Michael Perry
ZombieShortbus
new GuiTextEditCtrl(PlayerName) { canSaveDynamicFields = "0"; Profile = "GuiTextEditProfile"; HorizSizing = "right"; VertSizing = "bottom"; position = "86 100"; Extent = "141 17"; MinExtent = "8 2"; canSave = "1"; Visible = "1"; Variable = "pref::Player::Name"; hovertime = "1000"; maxLength = "1024"; historySize = "0"; password = "0"; tabComplete = "0"; sinkAllKeyEvents = "0"; password = "0"; passwordMask = "*"; };Standard Torque. When you click in the field to edit, the keyboard automatically pops up.