530 lines
15 KiB
C++
530 lines
15 KiB
C++
/*
|
|
* Dillo Widget
|
|
*
|
|
* Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
|
|
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
|
|
#include "core.hh"
|
|
#include "../lout/debug.hh"
|
|
|
|
#include <stdio.h>
|
|
|
|
namespace dw {
|
|
namespace core {
|
|
namespace ui {
|
|
|
|
using namespace lout;
|
|
using namespace lout::object;
|
|
|
|
Embed::Embed(Resource *resource)
|
|
{
|
|
DBG_OBJ_CREATE ("dw::core::ui::Embed");
|
|
registerName ("dw::core::ui::Embed", typeid(*this));
|
|
this->resource = resource;
|
|
resource->setEmbed (this);
|
|
DBG_OBJ_ASSOC_CHILD (resource);
|
|
}
|
|
|
|
Embed::~Embed()
|
|
{
|
|
delete resource;
|
|
DBG_OBJ_DELETE ();
|
|
}
|
|
|
|
void Embed::sizeRequestSimpl (Requisition *requisition)
|
|
{
|
|
resource->sizeRequest (requisition);
|
|
// TODO Correction should perhaps be left to the resources.
|
|
correctRequisition(requisition, core::splitHeightPreserveAscent, true, true);
|
|
}
|
|
|
|
void Embed::getExtremesSimpl (Extremes *extremes)
|
|
{
|
|
resource->getExtremes (extremes);
|
|
correctExtremes (extremes, false);
|
|
extremes->adjustmentWidth =
|
|
std::min (extremes->minWidthIntrinsic, extremes->minWidth);
|
|
}
|
|
|
|
void Embed::sizeAllocateImpl (Allocation *allocation)
|
|
{
|
|
resource->sizeAllocate (allocation);
|
|
}
|
|
|
|
int Embed::getAvailWidthOfChild (Widget *child, bool forceValue)
|
|
{
|
|
return resource->getAvailWidthOfChild (child, forceValue);
|
|
}
|
|
|
|
int Embed::getAvailHeightOfChild (Widget *child, bool forceValue)
|
|
{
|
|
return resource->getAvailHeightOfChild (child, forceValue);
|
|
}
|
|
|
|
void Embed::correctRequisitionOfChild (Widget *child,
|
|
Requisition *requisition,
|
|
void (*splitHeightFun) (int, int*, int*),
|
|
bool allowDecreaseWidth,
|
|
bool allowDecreaseHeight)
|
|
{
|
|
resource->correctRequisitionOfChild (child, requisition, splitHeightFun,
|
|
allowDecreaseWidth,
|
|
allowDecreaseHeight);
|
|
}
|
|
|
|
void Embed::correctExtremesOfChild (Widget *child, Extremes *extremes,
|
|
bool useAdjustmentWidth)
|
|
{
|
|
resource->correctExtremesOfChild (child, extremes, useAdjustmentWidth);
|
|
}
|
|
|
|
void Embed::containerSizeChangedForChildren ()
|
|
{
|
|
DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
|
|
resource->containerSizeChangedForChildren ();
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
void Embed::enterNotifyImpl (core::EventCrossing *event)
|
|
{
|
|
resource->emitEnter();
|
|
Widget::enterNotifyImpl(event);
|
|
}
|
|
|
|
void Embed::leaveNotifyImpl (core::EventCrossing *event)
|
|
{
|
|
resource->emitLeave();
|
|
Widget::leaveNotifyImpl(event);
|
|
}
|
|
|
|
bool Embed::buttonPressImpl (core::EventButton *event)
|
|
{
|
|
bool handled;
|
|
|
|
if (event->button == 3) {
|
|
resource->emitClicked(event);
|
|
handled = true;
|
|
} else {
|
|
handled = false;
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
void Embed::setDisplayed (bool displayed)
|
|
{
|
|
resource->setDisplayed (displayed);
|
|
}
|
|
|
|
void Embed::setEnabled (bool enabled)
|
|
{
|
|
resource->setEnabled (enabled);
|
|
}
|
|
|
|
void Embed::draw (View *view, Rectangle *area, DrawingContext *context)
|
|
{
|
|
drawWidgetBox (view, area, false);
|
|
resource->draw (view, area, context);
|
|
}
|
|
|
|
Iterator *Embed::iterator (Content::Type mask, bool atEnd)
|
|
{
|
|
return resource->iterator (mask, atEnd);
|
|
}
|
|
|
|
void Embed::setStyle (style::Style *style)
|
|
{
|
|
resource->setStyle (style);
|
|
Widget::setStyle (style);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool Resource::ActivateEmitter::emitToReceiver (lout::signal::Receiver
|
|
*receiver,
|
|
int signalNo,
|
|
int argc, Object **argv)
|
|
{
|
|
ActivateReceiver *ar = (ActivateReceiver*)receiver;
|
|
Resource *res = (Resource*)((Pointer*)argv[0])->getValue ();
|
|
|
|
switch (signalNo) {
|
|
case 0:
|
|
ar->activate (res);
|
|
break;
|
|
case 1:
|
|
ar->enter (res);
|
|
break;
|
|
case 2:
|
|
ar->leave (res);
|
|
break;
|
|
default:
|
|
misc::assertNotReached ();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Resource::ActivateEmitter::emitActivate (Resource *resource)
|
|
{
|
|
Pointer p (resource);
|
|
Object *argv[1] = { &p };
|
|
emitVoid (0, 1, argv);
|
|
}
|
|
|
|
void Resource::ActivateEmitter::emitEnter (Resource *resource)
|
|
{
|
|
Pointer p (resource);
|
|
Object *argv[1] = { &p };
|
|
emitVoid (1, 1, argv);
|
|
}
|
|
|
|
void Resource::ActivateEmitter::emitLeave (Resource *resource)
|
|
{
|
|
Pointer p (resource);
|
|
Object *argv[1] = { &p };
|
|
emitVoid (2, 1, argv);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Resource::~Resource ()
|
|
{
|
|
DBG_OBJ_DELETE ();
|
|
}
|
|
|
|
void Resource::setEmbed (Embed *embed)
|
|
{
|
|
this->embed = embed;
|
|
}
|
|
|
|
void Resource::getExtremes (Extremes *extremes)
|
|
{
|
|
DBG_OBJ_ENTER0 ("resize", 0, "getExtremes");
|
|
|
|
/* Simply return the requisition width */
|
|
Requisition requisition;
|
|
sizeRequest (&requisition);
|
|
extremes->minWidth = extremes->maxWidth = requisition.width;
|
|
extremes->minWidthIntrinsic = extremes->minWidth;
|
|
extremes->maxWidthIntrinsic = extremes->maxWidth;
|
|
|
|
DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
|
|
extremes->minWidth, extremes->maxWidth);
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
void Resource::sizeAllocate (Allocation *allocation)
|
|
{
|
|
}
|
|
|
|
int Resource::getAvailWidthOfChild (Widget *child, bool forceValue)
|
|
{
|
|
// Only used when the resource contains other dillo widgets.
|
|
misc::notImplemented ("Resource::getAvailWidthOfChild");
|
|
return 0;
|
|
}
|
|
|
|
int Resource::getAvailHeightOfChild (Widget *child, bool forceValue)
|
|
{
|
|
// Only used when the resource contains other dillo widgets.
|
|
misc::notImplemented ("Resource::getAvailHeightOfChild");
|
|
return 0;
|
|
}
|
|
|
|
void Resource::correctRequisitionOfChild (Widget *child,
|
|
Requisition *requisition,
|
|
void (*splitHeightFun) (int, int*,
|
|
int*),
|
|
bool allowDecreaseWidth,
|
|
bool allowDecreaseHeight)
|
|
{
|
|
// Only used when the resource contains other dillo widgets.
|
|
misc::notImplemented ("Resource::correctRequisitionOfChild");
|
|
}
|
|
|
|
void Resource::correctExtremesOfChild (Widget *child, Extremes *extremes,
|
|
bool useAdjustmentWidth)
|
|
{
|
|
// Only used when the resource contains other dillo widgets.
|
|
misc::notImplemented ("Resource::correctExtremesOfChild");
|
|
}
|
|
|
|
void Resource::containerSizeChangedForChildren ()
|
|
{
|
|
// No children by default.
|
|
}
|
|
|
|
void Resource::setDisplayed (bool displayed)
|
|
{
|
|
}
|
|
|
|
void Resource::draw (View *view, Rectangle *area, DrawingContext *context)
|
|
{
|
|
}
|
|
|
|
void Resource::setStyle (style::Style *style)
|
|
{
|
|
}
|
|
|
|
void Resource::emitEnter ()
|
|
{
|
|
activateEmitter.emitEnter(this);
|
|
}
|
|
|
|
void Resource::emitLeave ()
|
|
{
|
|
activateEmitter.emitLeave(this);
|
|
}
|
|
|
|
bool Resource::ClickedEmitter::emitToReceiver(lout::signal::Receiver *receiver,
|
|
int signalNo, int argc,
|
|
Object **argv)
|
|
{
|
|
((ClickedReceiver*)receiver)
|
|
->clicked ((Resource*)((Pointer*)argv[0])->getValue (),
|
|
(EventButton*)((Pointer*)argv[1])->getValue());
|
|
return false;
|
|
}
|
|
|
|
void Resource::ClickedEmitter::emitClicked (Resource *resource,
|
|
EventButton *event)
|
|
{
|
|
Pointer p1 (resource);
|
|
Pointer p2 (event);
|
|
Object *argv[2] = { &p1, &p2 };
|
|
emitVoid (0, 2, argv);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Iterator *LabelButtonResource::iterator (Content::Type mask, bool atEnd)
|
|
{
|
|
/** \todo Perhaps in brackets? */
|
|
// return new TextIterator (getEmbed (), mask, atEnd, getLabel ());
|
|
/** \bug Not implemented. */
|
|
return new EmptyIterator (getEmbed (), mask, atEnd);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void ComplexButtonResource::LayoutReceiver::resizeQueued (bool extremesChanged)
|
|
{
|
|
DBG_OBJ_ENTER ("resize", 0, "LayoutReceiver::resizeQueued", "%s",
|
|
extremesChanged ? "true" : "false");
|
|
resource->queueResize (extremesChanged);
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
ComplexButtonResource::ComplexButtonResource ()
|
|
{
|
|
DBG_OBJ_CREATE ("dw::core::ui::ComplexButtonResource");
|
|
layout = NULL;
|
|
layoutReceiver.resource = this;
|
|
click_x = click_y = -1;
|
|
}
|
|
|
|
void ComplexButtonResource::init (std::unique_ptr< Widget > widget)
|
|
{
|
|
childWidget = widget.get();
|
|
|
|
/* FIXME: Buttons should not need a full Layout */
|
|
layout = new Layout (createPlatform (), false);
|
|
setLayout (layout);
|
|
DBG_OBJ_ASSOC_CHILD (layout);
|
|
layout->setWidget (std::move( widget ));
|
|
layout->connect (&layoutReceiver);
|
|
|
|
if (getEmbed ())
|
|
childWidget->setQuasiParent (getEmbed ());
|
|
}
|
|
|
|
void ComplexButtonResource::setEmbed (Embed *embed)
|
|
{
|
|
ButtonResource::setEmbed (embed);
|
|
|
|
if (childWidget)
|
|
childWidget->setQuasiParent (getEmbed ());
|
|
}
|
|
|
|
ComplexButtonResource::~ComplexButtonResource ()
|
|
{
|
|
delete layout;
|
|
DBG_OBJ_DELETE ();
|
|
}
|
|
|
|
void ComplexButtonResource::sizeRequest (Requisition *requisition)
|
|
{
|
|
DBG_OBJ_ENTER0 ("resize", 0, "sizeRequest");
|
|
|
|
Requisition widgetRequisition;
|
|
childWidget->sizeRequest (&widgetRequisition);
|
|
requisition->width = widgetRequisition.width + 2 * reliefXThickness ();
|
|
requisition->ascent = widgetRequisition.ascent + reliefYThickness ();
|
|
requisition->descent = widgetRequisition.descent + reliefYThickness ();
|
|
|
|
DBG_OBJ_MSGF ("resize", 1, "result: %d * (%d + %d)",
|
|
requisition->width, requisition->ascent, requisition->descent);
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
void ComplexButtonResource::getExtremes (Extremes *extremes)
|
|
{
|
|
DBG_OBJ_ENTER0 ("resize", 0, "getExtremes");
|
|
|
|
Extremes widgetExtremes;
|
|
childWidget->getExtremes (&widgetExtremes);
|
|
extremes->minWidth = widgetExtremes.minWidth + 2 * reliefXThickness ();
|
|
extremes->maxWidth = widgetExtremes.maxWidth + 2 * reliefXThickness ();
|
|
extremes->minWidthIntrinsic = extremes->minWidth;
|
|
extremes->maxWidthIntrinsic = extremes->maxWidth;
|
|
|
|
DBG_OBJ_MSGF ("resize", 1, "result: %d / %d",
|
|
extremes->minWidth, extremes->maxWidth);
|
|
DBG_OBJ_LEAVE ();
|
|
}
|
|
|
|
void ComplexButtonResource::sizeAllocate (Allocation *allocation)
|
|
{
|
|
}
|
|
|
|
int ComplexButtonResource::getAvailWidthOfChild (Widget *child, bool forceValue)
|
|
{
|
|
int embedWidth = getEmbed()->getAvailWidth (forceValue);
|
|
if (embedWidth == -1)
|
|
return -1;
|
|
else
|
|
return std::max (embedWidth - 2 * reliefXThickness (), 0);
|
|
}
|
|
|
|
int ComplexButtonResource::getAvailHeightOfChild (Widget *child,
|
|
bool forceValue)
|
|
{
|
|
int embedHeight = getEmbed()->getAvailHeight (forceValue);
|
|
if (embedHeight == -1)
|
|
return -1;
|
|
else
|
|
return std::max (embedHeight - 2 * reliefYThickness (), 0);
|
|
}
|
|
|
|
void ComplexButtonResource::correctRequisitionOfChild (Widget *child,
|
|
Requisition *requisition,
|
|
void (*splitHeightFun)
|
|
(int, int*, int*),
|
|
bool allowDecreaseWidth,
|
|
bool allowDecreaseHeight)
|
|
{
|
|
// Similar to Widget::correctRequisitionOfChild, but for percentage
|
|
// the relief has to be considered.
|
|
|
|
if (style::isPerLength (child->getStyle()->width)) {
|
|
/* FIXME: Typo for getAvailWidth()? */
|
|
int availWidth = getEmbed()->getAvailHeight (false);
|
|
if (availWidth != -1) {
|
|
int baseWidth = std::max (availWidth
|
|
- getEmbed()->boxDiffWidth ()
|
|
- 2 * reliefXThickness (),
|
|
0);
|
|
int newWidth =
|
|
child->applyPerWidth (baseWidth, child->getStyle()->width);
|
|
requisition->width = allowDecreaseWidth ?
|
|
newWidth : std::max (requisition->width, newWidth);
|
|
}
|
|
} else
|
|
getEmbed()->correctReqWidthOfChildNoRec (child, requisition,
|
|
allowDecreaseWidth);
|
|
|
|
// TODO Percentage heights are ignored again.
|
|
getEmbed()->correctReqHeightOfChildNoRec(child, requisition,
|
|
splitHeightFun, allowDecreaseWidth);
|
|
|
|
}
|
|
|
|
void ComplexButtonResource::correctExtremesOfChild (Widget *child,
|
|
Extremes *extremes,
|
|
bool useAdjustmentWidth)
|
|
{
|
|
// Similar to Widget::correctExtremesOfChild, but for percentage
|
|
// the relief has to be considered.
|
|
|
|
if (style::isPerLength (child->getStyle()->width)) {
|
|
int availWidth = getEmbed()->getAvailHeight (false);
|
|
if (availWidth != -1) {
|
|
int baseWidth = std::max (availWidth
|
|
- getEmbed()->boxDiffWidth ()
|
|
- 2 * reliefXThickness (),
|
|
0);
|
|
extremes->minWidth = extremes->maxWidth =
|
|
child->applyPerWidth (baseWidth, child->getStyle()->width);
|
|
}
|
|
} else
|
|
getEmbed()->correctExtremesOfChildNoRec (child, extremes,
|
|
useAdjustmentWidth);
|
|
}
|
|
|
|
void ComplexButtonResource::containerSizeChangedForChildren ()
|
|
{
|
|
layout->containerSizeChanged ();
|
|
}
|
|
|
|
Iterator *ComplexButtonResource::iterator (Content::Type mask, bool atEnd)
|
|
{
|
|
/**
|
|
* \bug Implementation.
|
|
* This is a bit more complicated: We have two layouts here.
|
|
*/
|
|
return new EmptyIterator (getEmbed (), mask, atEnd);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Iterator *TextResource::iterator (Content::Type mask, bool atEnd)
|
|
{
|
|
// return new TextIterator (getEmbed (), mask, atEnd, getText ());
|
|
/** \bug Not implemented. */
|
|
return new EmptyIterator (getEmbed (), mask, atEnd);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Iterator *CheckButtonResource::iterator (Content::Type mask, bool atEnd)
|
|
{
|
|
//return new TextIterator (getEmbed (), mask, atEnd,
|
|
// isActivated () ? "[X]" : "[ ]");
|
|
/** \bug Not implemented. */
|
|
return new EmptyIterator (getEmbed (), mask, atEnd);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
RadioButtonResource::GroupIterator::~GroupIterator ()
|
|
{
|
|
}
|
|
|
|
Iterator *RadioButtonResource::iterator (Content::Type mask, bool atEnd)
|
|
{
|
|
//return new TextIterator (getEmbed (), mask, atEnd,
|
|
// isActivated () ? "(*)" : "( )");
|
|
/** \bug Not implemented. */
|
|
return new EmptyIterator (getEmbed (), mask, atEnd);
|
|
}
|
|
|
|
} // namespace ui
|
|
} // namespace core
|
|
} // namespace dw
|
|
|