WebView

A quick sample using the WebView

by David Walters

Carbon still doesn't have a "native" web viewer, but with a little tinkering you can make it "feel" like you didn't use any Cocoa. Sooner or later you will learn ObjC but right now you just want to know where to send your output. You've written something in Standard C++ somewhere else and you just want to give it a bare-bones gui, for example. A textbox for commands, an HTML pane for output.

Building one of these blended projects is a bit of a task for a green Mac programmer, and when the "just work!" levels are high, it helps to know about the /Developer/Examples folder.

This example will demonstrate giving your existing work, which already contains a beautiful HTML-formatted outputter, a Carbon-Cocoa gui that should "just work" in a few minutes.

Most of the work has already been done, so fire up the finder and go to /Developer/Examples/WebKit/CarbonWeb - make a copy of that folder in whatever folder you use to store your projects, and we're nearly finished.

I tried to do this a few different ways before I realised the concept of groups in Xcode. Once I had that down it was a snap to insert existing C++ into a Cocoa-based GUI.

Assuming you have your new copy of CarbonWeb open, and you've already had a bit of a command-y with it and seen that it has a textbox for user input, and an HTML window which displays output. This is what you want.

But what we really want is a way of taking a command, performing some calc, and outputting a report to a string, and sending that string to the WebView. No messy URL stuff, though come to think of it, it would be nice to have links to documentation from my Unit Testing app, so let's just extend the CarbonWeb app a little, and you can take care of stripping out the unwanted bits later.

I begin by adding a new group to the "Groups and Files" tree, by right-clicking the root node of that tree (which represents the current project), and selecting Add --> New Group.

We'll call the group "omicron" because "chi" would be corny.

That adds a folder to the tree, where I can safely tuck any C++ stuff I might like to use in my app. For today's example, I'm going to use the Programming Menus example in a slightly different way, and print an HTML formatted list of the menu structure. Still using char stars at heart, my new function will be declared in a file called "MenuTest.h", and defined in "MenuTest.cpp" inside your omicron group. (notice I didn't call it a folder?)

Before we get into the test function, we have to make sure our infrastructure will do this.

1 Open NIB Files/main.nib with Interface Builder and add a MenuItem to the File menu. Label it "TestHTML" and give it a commandID of 'HTST'. Save, close and return to Xcode.

2 Now open TWebWindow.cp (in the Sources group) and add this line at #36

#include "./MenuTest.h"

At #370 add this function:

//-------------------------------------------------------------------------------------
//	TWebWindow::TestHTML
//-------------------------------------------------------------------------------------
//	Tests the functionality of the WebFrame loadHTMLString() member with c++
//
void
TWebWindow::TestHTML(void) {
  WebFrame* 		mainFrame;
  mainFrame = [fController mainFrame];
  CFStringRef cref = CFStringCreateWithCString(
                                               kCFAllocatorDefault,
                                               PrintMenuStructureHTML().c_str(),
                                               kCFStringEncodingUTF8);
  [mainFrame loadHTMLString:(NSString *)cref baseURL:NULL];
  CFRelease(cref);
}

Whew, that's the Objective-C over with. The single line of Objective-C we were forced to write for ourselves. I guess that's why people are always making classes to wrap this stuff.

Now we have to open "TWebWindow.h", and insert the following at #50 (under LoadRequest ):

    void TestHTML( void );

Now, to TWindow.cp, for the TWindow command handler.

And, we have to add our event handler case into the switch at line 545 (which looks like this)

	switch( command.commandID )
	{

Add these lines above the first case, at line 547.

    case 'HTST': {
      TestHTML();      
      break;
    }

That traps any commandIDs which contain 'HTST' which is the commandID we gave our new menuitem, so the communication infrastructure has been enhanced to include this new menuitem.

(We could have done this programmatically but you could read Programming Menus and do that yourself - I did!)

Now it's time to actually add our function. Open MenuTest.h (in the omicron group - or right-click and Add-->New File and choose C++ Header.

You could choose C++ File and have the header created for you if you know you can do that. ([Buy quality movies])

Add the following lines to MenuTest.h:

#ifndef __MENUTEST_H__
#define __MENUTEST_H__
#include <string>
std::string PrintMenuStructureHTML(void);
#endif

I use header guards for sentimental reasons, but I've seen a few #pragma once directives kicking around in Mac land so you might like to use that.

Now open MenuTest.cpp and paste this in:

std::string PrintMenuStructureHTML(void){
  char * menuStrings[5] = {"one", "two", "three", "four", "five"};
  char * submenuStrings[5][2] = {
  {"1subone", "1subtwo"},
  {"2subone", "2subtwo"},
  {"3subone", "3subtwo"},
  {"4subone", "4subtwo"},
  {"5subone", "5subtwo"}
  };
  std::ostringstream outstr;
  outstr << "<html><head><title>Menu Structure</title>"
    << "<link rel=\"stylesheet\" charset=\"UTF-8\" type=\"text/css\" href=\"./style.css\">"
/*    << "<style type=\"text/css\">@import url(./style.css);</style>"*/
    << "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" >"
    << "</head><body>\n";
  outstr << "<table style=\"background-color: aliceblue;\">";
  for (int i = 0; i < 5; ++i) {
    outstr << "<tr><td>" << menuStrings[i] << "</td></tr><tr><td><table><tr>";
    for (int j = 0; j < 2; ++j) {
      outstr << "<td>" << submenuStrings[i][j] << "</td>";
    }
    outstr << "</tr></table></td></tr>";
  }
  outstr << "</table></body></html>" << std::endl;
  return outstr.str();
}

We are ready to build.

Note that I have commented out the lines that attempt to load a stylesheet, because neither of them seem to work. Hopefully one of us will figure it out. However inline styles work just fine, as you can see.

Command-y this little app and you will see the lines of communication between standard C++ and Cocoa-in-Carbon in action.

Here we have demostrated the minimum skills required to use a WebView from C++. I even used an actual std::string for this example, to prove it can be done.