Newer
Older
Import / research / ui / toolkit / test / main.cpp
// Project Headers
#include "Application.h"
#include "Window.h"
#include "Painter.h"
#include "SignalSlot.h"
#include "Widget.h"
#include "MemoryMapping.h"
#include "CommonWidgets.h"
#include "Test.h"
#include "XmlParser.h"
#include "UiBuilder.h"
#include "Reflection.h"

#include <iostream>
#include <iomanip>
#include <climits>
#include <fstream>
#include <cctype>
#include <cstdarg>

#include <type_traits>
#define typeof(...)    std::remove_reference<decltype(__VA_ARGS__)>::type

extern bool c_useRetina;
extern float c_retinaScale;

// TODO: doxygen style documentation everywhere


USING_NAMESPACE


class CludgeBoolProperty : public AbstractProperty<bool>
{
public:
  void cludgeSet(bool) { set(false); } // set the value without invoking valueChanged
};


// Useful for radio button groups
// pass in a list of Propery<bool> pointers, and terminate the argument list with NULL
void makeExclusiveBoolPropertySet(AbstractProperty<bool>* a_value1, ...)
{
	va_list list;

  std::vector<int> v;
  std::string str;

   
	va_start(list, a_value1);
	Vector<CludgeBoolProperty*> radioButtonGroup;
	CludgeBoolProperty* nextVal = (CludgeBoolProperty*)a_value1;
	while (nextVal) {
		radioButtonGroup.push_back(nextVal);
		nextVal = va_arg(list, CludgeBoolProperty*);
	}
	va_end(list);

	for (unsigned i = 0; i < radioButtonGroup.size(); i++)
		for (unsigned j = 0; j < radioButtonGroup.size(); j++)
			if (i != j)
        connect(radioButtonGroup[i]->valueChanged, radioButtonGroup[j], &CludgeBoolProperty::cludgeSet);
}


class TestWindow : public Inherit<Widget, TestWindow>
{
public:
  Property<String> m_labelText;
  Property<String> m_groupBoxTitle;

  Property<bool> m_checkBoxChecked1;
  Property<bool> m_radioButtonChecked1;
  Property<bool> m_checkBoxChecked2;
  Property<bool> m_radioButtonChecked2;
  Property<bool> m_checkBoxChecked3;
  Property<bool> m_radioButtonChecked3;
  Property<bool> m_checkBoxChecked4;
  Property<bool> m_radioButtonChecked4;

  Property<int>  m_sliderValue;
  Property<int>  m_comboValue;

  VBox* m_vbox1 = nullptr;

  void buttonPressed(int)
  {
    printf("button pressed\n");
  }

  TestWindow(Widget* parent, const char* name)
    : BaseT(parent, name)
  {
    XmlParser parser;
    XmlDomTreeBuilder domTree;
    UiBuilder uiBuilder(this);

    uiBuilder.registerProperty("m_labelText"          , m_labelText);
    uiBuilder.registerProperty("m_groupBoxTitle"      , m_groupBoxTitle);
    uiBuilder.registerProperty("m_checkBoxChecked1"	  , m_checkBoxChecked1);
    uiBuilder.registerProperty("m_radioButtonChecked1", m_radioButtonChecked1);
    uiBuilder.registerProperty("m_checkBoxChecked2"	  , m_checkBoxChecked2);
    uiBuilder.registerProperty("m_radioButtonChecked2", m_radioButtonChecked2);
    uiBuilder.registerProperty("m_checkBoxChecked3"   , m_checkBoxChecked3);
    uiBuilder.registerProperty("m_radioButtonChecked3", m_radioButtonChecked3);
    uiBuilder.registerProperty("m_checkBoxChecked4"   , m_checkBoxChecked4);
    uiBuilder.registerProperty("m_radioButtonChecked4", m_radioButtonChecked4);
    uiBuilder.registerProperty("m_sliderValue"		    , m_sliderValue);
    uiBuilder.registerProperty("m_comboValue"		      , m_comboValue);

    parser.parseXmlFile("../data/TestUI.xml", domTree);
    parser.parseXmlFile("../data/TestUI.xml", uiBuilder);
    domTree.printDomTree();

    Button* button = (Button*)uiBuilder.findWidget("but1").m_widget;
    RadioButton* rb1 = (RadioButton*)uiBuilder.findWidget("rb1").m_widget;
    RadioButton* rb2 = (RadioButton*)uiBuilder.findWidget("rb2").m_widget;
    RadioButton* rb3 = (RadioButton*)uiBuilder.findWidget("rb3").m_widget;
    RadioButton* rb4 = (RadioButton*)uiBuilder.findWidget("rb4").m_widget;

    if (button)
      connect(button->activated, this, &TestWindow::buttonPressed);
    
    if (rb1 && rb2 && rb3 && rb4)
      makeExclusiveBoolPropertySet(&rb1->checked, &rb2->checked, &rb3->checked, &rb4->checked, nullptr);

/*
    m_labelText = "testing";

    VBox *vbox1 = new VBox(this);
    HBox *hbox = new HBox(vbox1);

    VBox *vbox = new VBox(hbox);
    RadioButton *rb1 = new RadioButton(vbox, m_radioButtonChecked1, "Hello World");
    RadioButton *rb2 = new RadioButton(vbox, m_radioButtonChecked2, "Hello World");
    RadioButton *rb3 = new RadioButton(vbox, m_radioButtonChecked3, "Hello World");
    RadioButton *rb4 = new RadioButton(vbox, m_radioButtonChecked4, "Hello World");
    Button *but1 = new Button(vbox, "Hello World");
    connect(but1->activated, this, &TestWindow::buttonPressed);
    Button *but2 = new Button(vbox, "Hello World");
    Button *but3 = new Button(vbox, "Hello World");
    Button *but4 = new Button(vbox, "Hello World");
    new VSpace(vbox);

    vbox = new VBox(hbox);
    CheckBox *cb1 = new CheckBox(vbox, m_checkBoxChecked1, "Hello World");
    CheckBox *cb2 = new CheckBox(vbox, m_checkBoxChecked2, "Hello World");
    CheckBox *cb3 = new CheckBox(vbox, m_checkBoxChecked3, "Hello World");		
    CheckBox *cb4 = new CheckBox(vbox, m_checkBoxChecked4, "Hello World");		
    Label *l1 = new Label(vbox, m_labelText);
    Label *l2 = new Label(vbox, m_labelText);
    Label *l3 = new Label(vbox, m_labelText);		
    Label *l4 = new Label(vbox, m_labelText);
    LineEdit *le1 = new LineEdit(vbox, m_labelText);
    new VSpace(vbox);
    new VSpace(vbox);

    vbox = new VBox(hbox);
    Slider *sl = new Slider(vbox, m_sliderValue);
    ComboBox *cb = new ComboBox(vbox, m_comboValue);
    GroupBox *gb = new GroupBox(vbox, m_labelText);
    new VSpace(vbox);

    CheckBox *cb5 = new CheckBox(gb, m_checkBoxChecked1, "Test");
    CheckBox *cb6 = new CheckBox(gb, m_checkBoxChecked2, "Test");
    new Label(gb, m_labelText);
    new LineEdit(gb, m_labelText);
    new Slider(gb, m_sliderValue);
    new VSpace(vbox);

    ProgressBar *pb = new ProgressBar(vbox1, m_sliderValue);
    new VSpace(vbox1);

    cb->addItem("blah item 1");
    cb->addItem("foo item 2");
    cb->addItem("bar item 3");

    rb2->disabled = true;
    but2->disabled = true;
    cb2->disabled = true;
    l2->disabled = true;

    rb3->isDefault = true;
    but3->isDefault = true;
    cb3->isDefault = true;

    rb4->hasFocus = true;
    but4->hasFocus = true;
    cb4->hasFocus = true;

    connect(m_checkBoxChecked1.valueChanged, &but1->isDefault, &Property<bool>::setValue);

    makeExclusiveBoolPropertySet(&m_radioButtonChecked1, &m_radioButtonChecked2,
        &m_radioButtonChecked3, &m_radioButtonChecked4, NULL);
        */

    //connect(m_checkBoxChecked1.valueChanged, this, & typeof(*this)::refresh );
    //connect(m_checkBoxChecked1.valueChanged, this, & TestWindow::refresh );
    m_vbox1 = (VBox*)uiBuilder.findWidget("vbox1").m_widget;
    connect(m_sliderValue.valueChanged, this, &ThisT::sliderMoved);
    connect(m_checkBoxChecked1.valueChanged, this, &ThisT::toggleRetina);
  }

  void toggleRetina(bool val)
  {
    refresh(val);
//    c_useRetina = val;
//    updateLayout();
  }
  
  void sliderMoved(int val)
  {
    fprintf(stderr, "slider val: %i\n", val);
    //c_retinaScale = (4.0f * val) / 100.0f;
    
    //scale = (2.0f * val) / 100.0f;
    if (m_vbox1)
      m_vbox1->scale = (2.0f * val) / 100.0f;

    updateLayout();
  }

  void refresh(bool use)
  {
    int w = width();
    int h = height();
    /*
    if (c_useRetina)
    {
      w /= c_retinaScale;
      h /= c_retinaScale;
    }
    */
    c_useRetina = use;
    updateLayout();
    //repaint();
    setNewSize(w+1, h+0);
    //setNewSize(width()-1, height());
  }
};


BEGIN_NAMESPACE
extern bool drawAAText;
//extern bool drawFill;
//extern bool drawOutline;
extern bool drawWholeOutline;
//extern bool drawControlPoints;
extern std::string fontOpt;
//extern int fixedFontSize;
END_NAMESPACE


class FontTestWindow : public Widget // Window
{
  Property<bool> m_useRetina;
  Property<bool> m_drawFill;
  Property<bool> m_drawControlPoints;
  Property<bool> m_drawDebugOutline;
  Property<bool> m_drawWholeOutline;
  Property<bool> m_drawAA;
  Property<bool> m_fontOpt;
  Property<int> m_sliderValue;
public:
  FontTestWindow(Widget* a_parent, const char* a_name)
    : Widget(a_parent, a_name)
  {
    //Widget* vbox = this;//new VBox(this);
    Widget* vbox = new VBox(this);
    new CheckBox(vbox, m_useRetina, "retina");
    new CheckBox(vbox, m_drawFill, "fill");
    new CheckBox(vbox, m_drawWholeOutline, "outline");
    new CheckBox(vbox, m_drawDebugOutline, "debug-outline");
    new CheckBox(vbox, m_drawControlPoints, "control points");
    new CheckBox(vbox, m_drawAA, "anti-alias");
    new CheckBox(vbox, m_fontOpt, "font");
    new Slider(vbox, m_sliderValue);
    m_sliderValue = 40;
    m_drawFill = true;
    connect(m_drawFill.valueChanged, this, &FontTestWindow::redraw);
    connect(m_useRetina.valueChanged, this, &FontTestWindow::refresh);
  }
  void refresh(bool use)
  {
    int w = width();
    int h = height();
    if (c_useRetina)
    {
      w /= c_retinaScale;
      h /= c_retinaScale;
    }
    c_useRetina = use;
    //updateLayout();
    //repaint();
    setNewSize(w+8, h+0);
    //setNewSize(width()-1, height());
  }
  void redraw(bool)
  {
    repaint();
  }
  void paintEvent(PaintEvent& a_event)
  {
    Painter p(this);
    p.setFontSize(m_sliderValue.value() * 3);
    p.setPen(0x000000);

    //NAMESPACE::fixedFontSize = 100;
    NAMESPACE::drawAAText = m_drawAA.value();
    //NAMESPACE::drawFill = m_drawFill.value();
    //NAMESPACE::drawOutline = m_drawDebugOutline.value();
    NAMESPACE::drawWholeOutline = m_drawWholeOutline.value();
    //NAMESPACE::drawControlPoints = m_drawControlPoints.value();
    NAMESPACE::fontOpt = m_fontOpt.value() ? "Arial" : "Tahoma";
    NAMESPACE::fontOpt = m_fontOpt.value() ? "Verdana" : "Tahoma";
    //NAMESPACE::fontOpt = "Andale Mono";

    //p.drawText(120, 150, "$@filHeoWrld");
    p.drawText(120, 150, "fill Hello World");
    p.drawText(120, 300, "$@AGQRXY");

    //NAMESPACE::fixedFontSize = m_sliderValue.value();
    //printf("font size: %i\n", NAMESPACE::fixedFontSize);
    NAMESPACE::drawAAText = true;
    //NAMESPACE::drawFill = true;
    //NAMESPACE::drawOutline = false;
    NAMESPACE::drawWholeOutline = false;
    //NAMESPACE::drawControlPoints = false;
    //NAMESPACE::fontOpt = "Arial";
    NAMESPACE::fontOpt = "Apple Chancery";
    NAMESPACE::fontOpt = "Andale Mono";
    NAMESPACE::fontOpt = "Nadeem";
    NAMESPACE::fontOpt = "Verdana";
    NAMESPACE::fontOpt = "Tahoma";
    NAMESPACE::fontOpt = "Trebuchet MS";
    //NAMESPACE::fontOpt = "Segoe UI";
  }
};



std::string getMethodName(const std::string& prettyFunction)
{
  size_t end = prettyFunction.find("(") - 1;
  size_t begin = prettyFunction.substr(0, end).rfind(" ") + 1;
  return prettyFunction.substr(begin, end - begin + 1);
}

// Some crude introspection of current class and function
#define DEBUG_FUNC \
    /*fprintf(stderr, "Class: -%s- ", typeid(*this).name()); */ \
    fprintf(stderr, "Function: -%s-\n", getMethodName(__PRETTY_FUNCTION__).c_str());



class LineTestWindow : public Widget
{
public:
  LineTestWindow(Widget* a_parent, const char* a_name)
    : Widget(a_parent, a_name)
  {
    DEBUG_FUNC
  }
  void paintEvent(PaintEvent& a_event)
  {
    DEBUG_FUNC
    Painter p(this);
    p.setPen(0x000000);
    //p.setPen(0xFFC0C0C0);

    int w = 400;//width();
    int h = 500;//height();
    int cx = w / 2; 
    int cy = h / 2;
    int radius = ((w > h) ? w : h) * 2;
    const int lines = 100;
    for (int i = 0; i < lines; i++)
    {
      int x2 = cx + radius * cos((2.0 * M_PI * i) / lines);
      int y2 = cy + radius * sin((2.0 * M_PI * i) / lines);
      p.drawLine(cx, cy, x2, y2);
      //p.drawLine(x2, y2, cx, cy);
    }
  }
};


// Different ways to express lambdas
#define L1(var, ret, args, body)            struct { ret operator() args const body } var   // Pre C++11 compatible
#define L2(var, ret, args, body)            std::function<ret args> var = [] args body      // Explicit C++11 way
#define L3(var, ret, args, body)            auto var = [] args body                         // Implicit C++11


// This is building up towards a generic RPC kind of mechanism
// Main problem will be pointers in argument lists - you can send the value of a pointer
// via RPC, but when the remote side de-references it, it will be meaningless. Perhaps
// need to prevent them. But also means can't pass in structs which contain them too.
// So a general struct which contains pointers in it can't be passed to the RPC function
// even if those pointers aren't used. Perhaps needs to just pass what is marked as
// reflectable through the interface.
// With the Lua bindings I did before, it is similar to RPC in that it built up an
// argument list from a function signature and captured the values to pass to something.
// Probably should refer to what I did there while doing this.
enum BasicValueType
{
  Null,
  Void,
  Bool,
  Int8,
  UInt8,
  Int16,
  UInt16,
  Int32,
  UInt32,
  Int64,
  UInt64,
  Float32,
  Float64,
  Pointer
};

enum ValueType
{
  Basic,
  Complex
};

struct BasicValue
{
  BasicValueType m_type;
  union {
    int64_t  m_null;
    int64_t  m_void;
    bool     m_bool;
    int8_t   m_int8;
    uint8_t  m_uint8;
    int16_t  m_int16;
    uint16_t m_uint16;
    int32_t  m_int32;
    uint32_t m_uint32;
    int64_t  m_int64;
    uint64_t m_uint64;
    float    m_float32;
    double   m_float64;
    void*    m_pointer;
  } m_value;
};

struct ComplexValue
{
  struct Value {
    ValueType m_type;
    union {
      BasicValue    m_basic;
      //ComplexValue& m_complex;
    } m_value;
  };
  std::vector<Value> m_members;
};

struct CallableInterface : public InterfaceBase
{
  virtual ComplexValue operator() (const ComplexValue& args) = 0;
};




/*
template <typename Sig>
struct L
{
  ret operator() args const
  {
    std::function<void(int)> f = p[](int) { };
  }
};
*/

void lambdaTests()
{
  L1(f1, void, (int a, int b), { printf("f1 args: %i %i\n", a, b); });
  L2(f2, void, (int a, int b), { printf("f2 args: %i %i\n", a, b); });
  L3(f3, void, (int a, int b), { printf("f3 args: %i %i\n", a, b); });
  auto f4 = [] (int a, int b) { printf("f4 args: %i %i\n", a, b); };   // Implicit vs Macros for comparison (2 characters less)
  // If macro name was simply 'L' then only 1 less character to type using the implicit C++11 way vs using the macro which
  // could be made to work with pre C++11 compilers
  f1(1, 2);
  f2(3, 4);
  f3(5, 6);
  f4(7, 8);
}


void nullableTests()
{
  struct NullTest { int x; };
  Nullable2<NullTest> nullTest;

  auto n1 = nullTest;
  n1 = n1;
  // int x = n1.value().x;  // Is meant to assert or static_assert
  NullTest newNullTest = NullTest{ 1 };

  // Unfortunately need to assign it to a new variable - kind of defeats the point
  auto n2 = nullTest = newNullTest;
  n2 = nullTest = newNullTest;
  int y = n2.value().x;

  // then want to call something:
  //  foo(n1);  // and the passed in argument will be of type Nullable<T,true> or Nullable<T,false>

  int x = y; y = x;
}


extern void fontEditTest();
extern void formFactorTest();


int main(int argc, char* argv[])
{
  // fontEditTest();

  formFactorTest();

  nullableTests();

  lambdaTests();

  runUnitTests(0);

  Application app;

  Reflection::registerObjectTypes();
  
  Object::dumpReflectionInformation();

  Object object;
  VBox vbox(0);
  Object& obj = object;
  Object& vb = vbox;

  printf("Object type id: %i\n", Object::staticTypeId());
  printf("Widget type id: %i\n", Widget::staticTypeId());
  printf("VBox   type id: %i\n", VBox::staticTypeId());
  printf("HBox   type id: %i\n", HBox::staticTypeId());

  printf("--------------------------\n");
  printf("Object inherits Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      obj.inherits<Object>(), obj.inherits<Widget>(), obj.inherits<HBox>(), obj.inherits<VBox>());
  printf("VBox inherits Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      vb.inherits<Object>(), vb.inherits<Widget>(), vb.inherits<HBox>(), vb.inherits<VBox>());
  printf("Object is Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      obj.is<Object>(), obj.is<Widget>(), obj.is<HBox>(), obj.is<VBox>());
  printf("VBox is Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      vb.is<Object>(), vb.is<Widget>(), vb.is<HBox>(), vb.is<VBox>());
  printf("--------------------------\n");
  printf("Object inherits Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      Object::staticInherits<Object>(), Object::staticInherits<Widget>(), Object::staticInherits<HBox>(), Object::staticInherits<VBox>());
  printf("VBox inherits Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      VBox::staticInherits<Object>(), VBox::staticInherits<Widget>(), VBox::staticInherits<HBox>(), VBox::staticInherits<VBox>());
  printf("Object is Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      Object::staticIs<Object>(), Object::staticIs<Widget>(), Object::staticIs<HBox>(), Object::staticIs<VBox>());
  printf("VBox is Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      VBox::staticIs<Object>(), VBox::staticIs<Widget>(), VBox::staticIs<HBox>(), VBox::staticIs<VBox>());
  printf("--------------------------\n");
  printf("Object inherits Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      object.inherits<Object>(), object.inherits<Widget>(), object.inherits<HBox>(), object.inherits<VBox>());
  printf("VBox inherits Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      vbox.inherits<Object>(), vbox.inherits<Widget>(), vbox.inherits<HBox>(), vbox.inherits<VBox>());
  printf("Object is Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      object.is<Object>(), object.is<Widget>(), object.is<HBox>(), object.is<VBox>());
  printf("VBox is Object: %i, Widget: %i, HBox: %i VBox: %i\n",
      vbox.is<Object>(), vbox.is<Widget>(), vbox.is<HBox>(), vbox.is<VBox>());
  printf("--------------------------\n");

  /*
  GenericFactory<ReflectionDataFactory> reflectionFactory;
  for (ReflectionDataFactory f : reflectionFactory)
  {
    const ReflectionDataInterface* i = f();
    if (i) {
      printf("Type: %i  Name: %s  Size: %i\n", i->GetTypeId(), i->GetType().c_str(), i->GetSize());
    } else {
      printf("Invalid reflection factory\n");
    }
  }
  */

  printf("sizeof(Object): %lu  sizeof(VBox): %lu\n", sizeof(Object), sizeof(VBox));

#if 1
  HBox b(0);
  Widget* p = &b;
  TestWindow testWin(p, "Test Window");
  Property<int> scrollValue;
  ScrollBar sb(p, scrollValue);
  b.setNewSize(800, 600);
#else
  //FontTestWindow testWin(0, "Test Window");
  //testWin.setNewSize(1200, 600);
  LineTestWindow testWin(0, "Test Window");
  testWin.setNewSize(400, 500);
#endif

  app.exec();
}


/*
#include <link.h>
#include <string>
#include <vector>

using namespace std;

int retrieve_symbolnames(struct dl_phdr_info* info, size_t info_size, void* symbol_names_vector) 
{
  ElfW(Dyn*) dyn;
  ElfW(Sym*) sym;
  ElfW(Word*) hash;

  char* strtab = 0;
  char* sym_name = 0;
  ElfW(Word) sym_cnt = 0;

  vector<string>* symbol_names = reinterpret_cast<vector<string>*>(symbol_names_vector);

  for (size_t header_index = 0; header_index < info->dlpi_phnum; header_index++)
  {
    if (info->dlpi_phdr[header_index].p_type == PT_DYNAMIC)
    {
      dyn = (ElfW(Dyn)*)(info->dlpi_addr +  info->dlpi_phdr[header_index].p_vaddr);
      while(dyn->d_tag != DT_NULL)
      {
        if (dyn->d_tag == DT_HASH)
        {
          hash = (ElfW(Word*))dyn->d_un.d_ptr;
          sym_cnt = hash[1];
        }
        else if (dyn->d_tag == DT_STRTAB)
        {
          strtab = (char*)dyn->d_un.d_ptr;
        }
        else if (dyn->d_tag == DT_SYMTAB)
        {
          sym = (ElfW(Sym*))dyn->d_un.d_ptr;
          for (ElfW(Word) sym_index = 0; sym_index < sym_cnt; sym_index++)
          {
            sym_name = &strtab[sym[sym_index].st_name];
            symbol_names->push_back(string(sym_name));
          }
        }
        dyn++;
      }
    }
  }
  return 1;
}

int main()
{
  vector<string> symbolNames;
  dl_iterate_phdr(retrieve_symbolnames, &symbolNames);
  return 0;
}
*/