Hey guys,
I have made a very very very simple wysiwyg editor that now operates on HTML syntax, but can very easily be remade to support a wiki syntax and so on.
[attachment=1]Picture 4.jpg[/attachment]
[attachment=0]Picture 5.jpg[/attachment]
i guess its not the most efficient thing in the world, but it does its job.
except for one thing: when i change a font size (for instance i click on the h1 button), the line height of the entire text editor is changed, and not just the line of the enlarged text. i am guessing this is a bug with the text editor, unless i’m doing something wrong. would be glad to hear your inputs!
here is the .h file:
#ifndef RICHEDITOR_H
#define RICHEDITOR_H
class RichEditor
: public Component,
public ButtonListener,
public SliderListener,
public KeyListener,
public AsyncUpdater
{
TextEditor * editor;
StringArray startTokens;
StringArray endTokens;
Array <Font> fonts;
Font font;
bool isReadOnly;
bool render;
TextButton * btn_bold;
TextButton * btn_under;
TextButton * btn_italic;
TextButton * btn_h1;
TextButton * btn_h2;
TextButton * btn_h3;
Slider * fontSize;
TextButton * btn_source;
public:
RichEditor::RichEditor(bool IsReadOnly);
RichEditor::~RichEditor();
void RichEditor::resized();
///////////////////////////////////
// Controls
///////////////////////////////////
//this is just to make sure we render when people copy or type
bool RichEditor::keyPressed (const KeyPress& key, Component* originatingComponent);
void RichEditor::handleAsyncUpdate();
bool RichEditor::keyPressed (const KeyPress& key);
void RichEditor::buttonClicked (Button* button);
void RichEditor::sliderValueChanged (Slider* slider);
void RichEditor::setFont(Font newFont);
///////////////////////////////////
// Rendering
///////////////////////////////////
void RichEditor::defineTokens();
void RichEditor::applyFormating(int formatType);
void RichEditor::renderText();
///////////////////////////////////
// Data Exchange
///////////////////////////////////
TextEditor * RichEditor::getEditor();
// this adds "<br>\n" instead of \n or alternatively just "<br>" to save space while compromising readability.
String RichEditor::getHTML(bool newLineOnBR=true);
// this is for transfering between rich editors without the <br>
String RichEditor::getText();
void RichEditor::setText(String text);
String RichEditor::getCleanText();
juce_UseDebuggingNewOperator
};
#endif
here is the .cpp file:
#include "juce.h"
#include "richEditor.h"
RichEditor::RichEditor (bool IsReadOnly)
:font(14.f),
isReadOnly(IsReadOnly),
render(true)
{
if (!IsReadOnly)
{
addAndMakeVisible(btn_bold = new TextButton("B","Bold Ctrl+b"));
btn_bold->setBounds(5,0,30,25);
btn_bold->addButtonListener(this);
addAndMakeVisible(btn_under = new TextButton("u", "Underline Ctrl+u"));
btn_under->setBounds(35,0,30,25);
btn_under->addButtonListener(this);
addAndMakeVisible(btn_italic = new TextButton("i","Bold Ctrl+i"));
btn_italic->setBounds(65,0,30,25);
btn_italic->addButtonListener(this);
addAndMakeVisible(btn_h1 = new TextButton("h1", "Headline Level 1 Ctrl+1"));
btn_h1->setBounds(95,0,30,25);
btn_h1->addButtonListener(this);
addAndMakeVisible(btn_h2 = new TextButton("h2","Headline Level 2 Ctrl+2"));
btn_h2->setBounds(125,0,30,25);
btn_h2->addButtonListener(this);
addAndMakeVisible(btn_h3 = new TextButton("h3", "Headline Level 3 Ctrl+3"));
btn_h3->setBounds(155,0,30,25);
btn_h3->addButtonListener(this);
addAndMakeVisible(btn_source = new TextButton("HTML", "Show HTML source Ctrl+h"));
btn_source->setBounds(185,0,60,25);
btn_source->addButtonListener(this);
btn_source->setClickingTogglesState(true);
addAndMakeVisible(fontSize = new Slider("Font size"));
fontSize->setRange(8., 48., .5);
fontSize->setBounds(250,5,100,15);
fontSize->addListener(this);
fontSize->setValue(14., false, false);
fontSize->setTextBoxStyle(Slider::TextBoxLeft, false, 30, 15);
}
addAndMakeVisible(editor = new TextEditor());
editor->setMultiLine(true, true);
editor->setReturnKeyStartsNewLine(true);
editor->setTabKeyUsedAsCharacter(true);
if (IsReadOnly)
{
editor->setReadOnly(true);
editor->setPopupMenuEnabled(false);
}
else
editor->addKeyListener(this);
defineTokens();
}
RichEditor::~RichEditor()
{
deleteAllChildren();
}
void RichEditor::resized()
{
if (isReadOnly)
editor->setBounds(0,0,getWidth(),getHeight());
else
editor->setBounds(0,30,getWidth(),getHeight()-30);
}
///////////////////////////////////
// Controls
///////////////////////////////////
bool RichEditor::keyPressed (const KeyPress& key, Component* originatingComponent)
{
triggerAsyncUpdate();
return false;
}
void RichEditor::handleAsyncUpdate()
{
renderText();
}
bool RichEditor::keyPressed (const KeyPress& key)
{
if (key == KeyPress (T('b'), ModifierKeys::commandModifier, 0))
{
applyFormating(1);
return true;
}
else if (key == KeyPress (T('u'), ModifierKeys::commandModifier, 0))
{
applyFormating(2);
return true;
}
else if (key == KeyPress (T('i'), ModifierKeys::commandModifier, 0))
{
applyFormating(3);
return true;
}
else if (key == KeyPress (T('1'), ModifierKeys::commandModifier, 0))
{
applyFormating(4);
return true;
}
else if (key == KeyPress (T('2'), ModifierKeys::commandModifier, 0))
{
applyFormating(5);
return true;
}
else if (key == KeyPress (T('3'), ModifierKeys::commandModifier, 0))
{
applyFormating(6);
return true;
}
else if (key == KeyPress (T('h'), ModifierKeys::commandModifier, 0))
{
applyFormating(-1);
return true;
}
return false;
}
void RichEditor::buttonClicked (Button* button)
{
if (button == btn_bold)
applyFormating(1);
else if (button == btn_under)
applyFormating(2);
else if (button == btn_italic)
applyFormating(3);
else if (button == btn_h1)
applyFormating(4);
else if (button == btn_h2)
applyFormating(5);
else if (button == btn_h3)
applyFormating(6);
else if (button == btn_source)
{
render = !btn_source->getToggleState();
applyFormating(-1);
}
editor->grabKeyboardFocus();
}
void RichEditor::sliderValueChanged (Slider* slider)
{
setFont(slider->getValue());
}
void RichEditor::setFont(Font newFont)
{
font = newFont;
defineTokens();
renderText();
}
///////////////////////////////////
// Rendering
///////////////////////////////////
void RichEditor::defineTokens()
{
startTokens.clear();
endTokens.clear();
fonts.clear();
startTokens.add("");
endTokens.add("");
fonts.add(font);
startTokens.add("<b>");
endTokens.add("</b>");
font.setBold(true);
fonts.add(font);
font.setBold(false);
startTokens.add("<u>");
endTokens.add("</u>");
font.setUnderline(true);
fonts.add(font);
font.setUnderline(false);
startTokens.add("<i>");
endTokens.add("</i>");
font.setItalic(true);
fonts.add(font);
font.setItalic(false);
startTokens.add("<h1>");
endTokens.add("</h1>");
float height = font.getHeight();
font.setHeight(height*2.f);
font.setBold(true);
fonts.add(font);
font.setBold(false);
font.setHeight(height);
startTokens.add("<h2>");
endTokens.add("</h2>");
font.setHeight(height*1.5f);
font.setBold(true);
fonts.add(font);
font.setBold(false);
font.setHeight(height);
startTokens.add("<h3>");
endTokens.add("</h3>");
font.setHeight(height*1.25f);
font.setBold(true);
fonts.add(font);
font.setBold(false);
font.setHeight(height);
}
void RichEditor::applyFormating(int formatType)
{
if (formatType > 0)
{
String text = editor->getHighlightedText();
if (text.length()>0)
editor->insertTextAtCursor(startTokens[formatType] + text + endTokens[formatType]);
}
renderText();
}
void RichEditor::renderText()
{
String text = editor->getText();
editor->applyFontToAllText(fonts[0]);
if (render)
{
String replace, startToken, endToken;
int index, indexEnd, startLength,endLength;
int carentPos = editor->getCaretPosition();
int size = startTokens.size();
for (int i = 1; i<size;i++)
{
startToken = startTokens[i];
endToken = endTokens[i];
index = text.indexOf(startToken);
indexEnd = text.indexOf(endToken);
startLength = startToken.length();
endLength = endToken.length();
while(index!=-1 && indexEnd!=-1 && index<indexEnd)
{
editor->setHighlightedRegion(index, indexEnd-index+endLength);
replace = editor->getHighlightedText().substring(startLength).dropLastCharacters(endLength);
editor->setFont(0);
editor->insertTextAtCursor(startToken);
editor->setFont(fonts[i]);
editor->insertTextAtCursor(replace);
editor->setFont(0);
editor->insertTextAtCursor(endToken);
text = editor->getText();
index = text.indexOf(indexEnd,startToken);
indexEnd = text.indexOf(indexEnd+1,endToken);
}
}
editor->setFont(fonts[0]);
editor->setCaretPosition(carentPos);
}
editor->resized();
//DBG(getHTML());
}
///////////////////////////////////
// Data Exchange
///////////////////////////////////
TextEditor * RichEditor::getEditor()
{
return editor;
}
String RichEditor::getHTML(bool newLineOnBR)
{
return editor->getText().replace(T("\n"),newLineOnBR ? T( "<br>\n"):T("<br>"), false);
}
String RichEditor::getText()
{
return editor->getText();
}
void RichEditor::setText(String text)
{
editor->setText(text,false);
renderText();
}
String RichEditor::getCleanText()
{
int size = startTokens.size();
String text = editor->getText();
for (int i = 1; i<size;i++)
{
text = text.replace(startTokens[i], String::empty, true);
text = text.replace(endTokens[i], String::empty, true);
}
return text;
}
enjoy!