Custom component in tableListBox


#1

Hi !

I’m trying to manage id3 tags directly from my tableListBox component which displays my songs library (like iTunes does for example).

I’m overriding the refreshComponentForCell method to create a custom TextEditor which allows to modify the existing tag when any cell is clicked.

I would like to launch my saveTag() method whenever another cell is clicked than the currently focused.
Is there any existing virtual method called when my custom component loses focus and before it gets deleted ?
(focusgained is never called because the component has already been deleted)

I hope my poor english is clear enough for you to help me … :wink:

thanks in advance !


#2

Yes - check out the Label component, it has lots of methods for handling exactly this kind of thing.


#3

allright I hadn’t seen Label were editable, that’s exactly what I’m looking for…

thanks a lot :smiley:


#4

I did the similar thing like this,

if (columnId == kColumnIdName) {
    String sLabelText = pXml->getStringAttribute(T("Name"));
    Label* pLabel = (Label*)existingComponentToUpdate;
    if (pLabel != NULL)
        pLabel->setText(sLabelText, false);
    else
        pLabel = new Label(String::empty, sLabelText);
    return pLabel;
}
else {
    //  for any other column, just return 0, as we'll be painting these columns directly.
    jassert(existingComponentToUpdate == NULL);
    return NULL;
}

I could get the name displayed in the label. But when I click the name column, the label component steals the mouse click and the line could not be selected.

What do you think about the good solution if I want the behavior like iTunes, Mac OS X Finder or Windows Explorer?

Masanao Hayashi


#5

You could use setEditable to only make the label editable on a double-click?


#6

In the general application such as Finder, Explorer and iTunes, double click is not the way to edit the name.

  • The first click is selecting the row.
  • Click the selected row and wait 0.5 sec, the label is turned to be edited.
  • Click the selected row and move mouse immediately, the label is turned to be edited.

I want to implement those functions. Now I’m trying to make custom compnent which inherits Label but the code wil be complex…

Masanao Hayashi


#7

After some trying, here is my current solution…

Use this component in TableListBoxModel::refreshComponentForCell() to make the TableListBox like iTunes.

//******************************************************************************
//	CListItemLabel.h
//******************************************************************************
#ifndef	__CListItemLabel_h__
#define __CListItemLabel_h__


#include "juce.h"


//==============================================================================
//	CListItemLabel
//==============================================================================
class CListItemLabel :
	public Label,
	public Timer
{
public:
	CListItemLabel(TableListBox* pOwner, const String& sLabelText = String::empty);

	//	Label overrides.
	virtual void mouseMove(const MouseEvent& e);
	virtual void mouseExit(const MouseEvent& e);
	virtual void mouseDown(const MouseEvent& e);
	virtual void mouseDrag(const MouseEvent& e);
	virtual void mouseUp(const MouseEvent& e);
protected:
	virtual void mouseDoubleClick(const MouseEvent& e);
public:
	virtual void textEditorReturnKeyPressed(TextEditor& ed);
	virtual void textEditorEscapeKeyPressed(TextEditor& ed);
	virtual void paint(Graphics& g);

	//	Timer overrides.
	virtual void timerCallback();

	int GetRow() { return m_iRow; }
	void SetRow(int iRow) { m_iRow = iRow; }
	int GetColumnId() { return m_iColumnId; }
	void SetColumnId(int iColumnId) { m_iColumnId = iColumnId; }
	void SetRowAndColumnId(int iRow, int iColumnId) { SetRow(iRow); SetColumnId(iColumnId); }

	void CreateEditor();
	void CleanupEditor();


private:
	TableListBox* m_pOwner;
	int		m_iRow;
	int		m_iColumnId;
	bool	m_bClickedtoEdit;
	int		m_iMouseDownX;
	int		m_iMouseDownY;
};
#endif	//	!__CListItemLabel_h__
//******************************************************************************
//	CListItemLabel.cpp
//******************************************************************************
#define __CListItemLabel_cpp__


#include "CListItemLabel.h"


CListItemLabel::CListItemLabel(TableListBox* pOwner, const String& sLabelText) :
	Label(T("CListItemLabel"), sLabelText),
	m_pOwner(pOwner),
	m_iRow(-1),
	m_iColumnId(-1),
	m_bClickedtoEdit(false)
{
}

void CListItemLabel::mouseMove(const MouseEvent& e)
{
	if (isTimerRunning()) {
		if ((abs(e.x - m_iMouseDownX) > 10) || (abs(e.x - m_iMouseDownX) > 10))
			CreateEditor();
	}

	Component* pComponent = getParentComponent();
	if (pComponent != NULL) {
		MouseEvent mEvent = e.getEventRelativeTo(pComponent);
		pComponent->mouseMove(mEvent);
	}
	Label::mouseMove(e);
}

void CListItemLabel::mouseExit(const MouseEvent& e)
{
	if (isTimerRunning())
		CreateEditor();
	Label::mouseExit(e);
}

void CListItemLabel::mouseDown(const MouseEvent& e)
{
	m_bClickedtoEdit = false;
	if (e.mods.isLeftButtonDown() &&
		(!e.mods.isAnyModifierKeyDown()) &&
		(e.getNumberOfClicks() == 1)) {

		SparseSet<int> aiSelectedRows = m_pOwner->getSelectedRows();
		if ((aiSelectedRows.size() == 1) && (aiSelectedRows[0] == GetRow())) {
			m_bClickedtoEdit = true;
			m_iMouseDownX = e.x;
			m_iMouseDownY = e.y;
		}
		else {
			aiSelectedRows.clear();
			aiSelectedRows.addRange(GetRow(), 1);
			m_pOwner->setSelectedRows(aiSelectedRows);

		}
	}

	Component* pComponent = getParentComponent();
	if (pComponent != NULL) {
		MouseEvent mEvent = e.getEventRelativeTo(pComponent);
		pComponent->mouseDown(mEvent);
	}
//	Label::mouseDown(e);
}

void CListItemLabel::mouseDrag(const MouseEvent& e)
{
	Component* pComponent = getParentComponent();
	if (pComponent != NULL) {
		MouseEvent mEvent = e.getEventRelativeTo(pComponent);
		pComponent->mouseDrag(mEvent);
	}
//	Label::mouseDrag(e);
}

void CListItemLabel::mouseUp(const MouseEvent& e)
{
	if (m_bClickedtoEdit) {
		m_bClickedtoEdit = false;
		startTimer(500);
	}

	Component* pComponent = getParentComponent();
	if (pComponent != NULL) {
		MouseEvent mEvent = e.getEventRelativeTo(pComponent);
		pComponent->mouseUp(mEvent);
	}
//	Label::mouseUp(e);
}

void CListItemLabel::mouseDoubleClick(const MouseEvent& e)
{
	Label::mouseDoubleClick(e);
	if (isTimerRunning())
		stopTimer();

	Component* pComponent = getParentComponent();
	if (pComponent != NULL) {
		MouseEvent mEvent = e.getEventRelativeTo(pComponent);
		pComponent->mouseDoubleClick(mEvent);
	}
}

void CListItemLabel::textEditorReturnKeyPressed(TextEditor& ed)
{
	Label::textEditorReturnKeyPressed(ed);
	CleanupEditor();
}

void CListItemLabel::textEditorEscapeKeyPressed(TextEditor& ed)
{
	Label::textEditorEscapeKeyPressed(ed);
	CleanupEditor();
}

void CListItemLabel::timerCallback()
{
	CreateEditor();
}

void CListItemLabel::paint(Graphics& g)
{
	if (!isEditable()) {
		Colour mTextColour = (m_pOwner->hasKeyboardFocus(true) && m_pOwner->isRowSelected(GetRow()))?
			Colours::white: Colours::black;
		setColour(Label::textColourId, mTextColour);
	}

	Label::paint(g);


	g.setColour(Colour(0x30000000));
	g.drawLine((float)(getWidth() - 1), 0, (float)(getWidth() - 1), (float)getHeight());
}

void CListItemLabel::CreateEditor()
{
	if (isTimerRunning())
		stopTimer();
	showEditor();
	setColour(Label::backgroundColourId,	Colours::white);
	setColour(Label::textColourId,			Colours::black);
}

void CListItemLabel::CleanupEditor()
{
	if (!isEditable()) {
		setColour(Label::backgroundColourId,	Colour(0x00000000));
		setColour(Label::textColourId,			Colours::black);
	}
}

#8