mirror of
https://github.com/thug1src/thug.git
synced 2025-01-21 21:33:46 +00:00
1375 lines
43 KiB
C++
1375 lines
43 KiB
C++
#include <core/defines.h>
|
|
#include <gel/scripting/checksum.h>
|
|
#include <gel/scripting/script.h>
|
|
#include <gel/scripting/struct.h>
|
|
#include <gel/scripting/array.h>
|
|
#include <gel/scripting/vecpair.h>
|
|
#include <gfx/NxFontMan.h>
|
|
#include <gfx/NxFont.h>
|
|
#include <gfx/2D/ScreenElement2.h>
|
|
#include <gfx/2D/ScreenElemMan.h>
|
|
#include <gfx/2D/TextElement.h>
|
|
#include <gfx/2D/BlurEffect.h>
|
|
#include <gfx/2D/Window.h>
|
|
|
|
#define BLUR_EFFECT_ON 1
|
|
|
|
namespace Front
|
|
{
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CTextElement::CTextElement() :
|
|
m_shadow_rgba(0, 0, 0, 128)
|
|
{
|
|
//Ryan("I am a new CTextElement\n");
|
|
m_font_checksum = 0;
|
|
mp_font = NULL;
|
|
mp_text = NULL;
|
|
mp_blur_effect = NULL;
|
|
m_use_shadow = false;
|
|
create_text_instances(1);
|
|
|
|
m_override_encoded_rgba = false;
|
|
m_previous_override_rgba_state = false;
|
|
|
|
SetType(CScreenElement::TYPE_TEXT_ELEMENT);
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CTextElement::~CTextElement()
|
|
{
|
|
//Ryan("Destroying CTextElement\n");
|
|
DetachBlurEffect();
|
|
destroy_text_instances();
|
|
if (mp_text)
|
|
delete mp_text;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::SetProperties(Script::CStruct *pProps)
|
|
{
|
|
CScreenElement::SetProperties(pProps);
|
|
|
|
uint32 font_crc;
|
|
if (pProps->GetChecksum(CRCD(0x2f6bf72d,"font"), &font_crc))
|
|
SetFont(font_crc);
|
|
Dbg_MsgAssert(m_font_checksum, ("no font loaded"));
|
|
|
|
const char *p_text;
|
|
if (pProps->GetText(CRCD(0xc4745838,"text"), &p_text))
|
|
SetText(p_text);
|
|
|
|
Dbg_MsgAssert(m_id != Obj::CBaseManager::vNO_OBJECT_ID, ("what, no ID"));
|
|
|
|
if (pProps->ContainsFlag(CRCD(0xd1653c6c,"blur_effect")))
|
|
AttachBlurEffect();
|
|
if (pProps->ContainsFlag(CRCD(0x4b73e4f8,"no_blur_effect")))
|
|
DetachBlurEffect();
|
|
|
|
Image::RGBA blur_rgba;
|
|
if (resolve_rgba(pProps, CRCD(0x4f0e1182,"blur_rgba"), &blur_rgba) && mp_blur_effect)
|
|
mp_blur_effect->SetRGBA(blur_rgba);
|
|
|
|
Script::CPair shadow_offs;
|
|
if (pProps->GetPair(CRCD(0x2a1fe0cc,"shadow_offs"), &shadow_offs))
|
|
{
|
|
SetShadowOff(shadow_offs.mX, shadow_offs.mY);
|
|
}
|
|
|
|
if (pProps->ContainsFlag(CRCD(0x8a897dd2,"shadow")))
|
|
SetShadowState(true);
|
|
if (pProps->ContainsFlag(CRCD(0x95b5b9a0,"no_shadow")))
|
|
SetShadowState(false);
|
|
|
|
resolve_rgba(pProps, CRCD(0x1e7bb1f5,"shadow_rgba"), &m_shadow_rgba);
|
|
|
|
if ( pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ) )
|
|
{
|
|
Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
|
|
m_previous_override_rgba_state = m_override_encoded_rgba;
|
|
}
|
|
if ( pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ) )
|
|
{
|
|
Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
|
|
m_override_encoded_rgba = m_previous_override_rgba_state;
|
|
}
|
|
|
|
if (pProps->ContainsFlag(CRCD(0xb7686f92,"override_encoded_rgba")))
|
|
m_override_encoded_rgba = true;
|
|
if (pProps->ContainsFlag(CRCD(0xd25bc6cd,"dont_override_encoded_rgba")))
|
|
m_override_encoded_rgba = false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::SetMorph(Script::CStruct *pProps)
|
|
{
|
|
if (mp_blur_effect)
|
|
{
|
|
mp_blur_effect->SetMorph(pProps);
|
|
}
|
|
|
|
CScreenElement::SetMorph(pProps);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::SetFont(uint32 font_checksum)
|
|
{
|
|
m_font_checksum = font_checksum;
|
|
mp_font = Nx::CFontManager::sGetFont(m_font_checksum);
|
|
Dbg_MsgAssert(Nx::CFontManager::sTestFontLoaded(m_font_checksum), ("font %s isn't loaded", Script::FindChecksumName(m_font_checksum)));
|
|
m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::SetText(const char *pText)
|
|
{
|
|
int new_length = strlen(pText) + 1;
|
|
Dbg_MsgAssert(new_length < vMAX_TEXT_LENGTH, ("string too long %d", new_length));
|
|
|
|
// if the old string is not big enough, then we need to allocate a new one
|
|
// otherwize, overwrite it, as allocations are slow
|
|
if (!mp_text || (int)(strlen(mp_text)) < new_length-1) // note, does not account for actual allocated size, but will work is string are same size
|
|
{
|
|
if (mp_text)
|
|
delete mp_text;
|
|
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
|
|
mp_text = new char[new_length];
|
|
Mem::Manager::sHandle().PopContext();
|
|
}
|
|
// copy pText to mp_text, replacing '\m' tags with '\b' tags
|
|
const char *p_in = pText;
|
|
char *p_out = mp_text;
|
|
while(*p_in)
|
|
{
|
|
if (*p_in == '\\' && *(p_in+1) == '\\')
|
|
{
|
|
// output "\\" tag unchanged
|
|
*p_out++ = *p_in++;
|
|
*p_out++ = *p_in++;
|
|
}
|
|
// ignore sticky space
|
|
else if (*p_in == '\\' && *(p_in+1) == '_')
|
|
{
|
|
p_in++;
|
|
p_in++;
|
|
*p_out++ = ' ';
|
|
}
|
|
else if (*p_in == '\\' && *(p_in+1) == 'm')
|
|
{
|
|
*p_out++ = *p_in++;
|
|
*p_out++ = 'b';
|
|
p_in++;
|
|
*p_out++ = Nx::CFontManager::sMapMetaCharacterToButton(p_in++);
|
|
}
|
|
else
|
|
{
|
|
*p_out++ = *p_in++;
|
|
}
|
|
}
|
|
*p_out = '\0';
|
|
|
|
if( mp_font )
|
|
{
|
|
float w, h;
|
|
mp_font->QueryString(mp_text, w, h); // width and height in pixels
|
|
|
|
if (!(m_object_flags & vFORCED_DIMS))
|
|
{
|
|
SetDims(w, h);
|
|
}
|
|
}
|
|
|
|
m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int CTextElement::GetLength()
|
|
{
|
|
return strlen( mp_text );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CTextElement::Concatenate( const char *pText )
|
|
{
|
|
int old_length = 0;
|
|
if ( mp_text )
|
|
old_length += strlen( mp_text );
|
|
int length = old_length + strlen( pText );
|
|
|
|
if ( length + 1 >= vMAX_TEXT_LENGTH )
|
|
{
|
|
// new string would be too long...
|
|
return false;
|
|
}
|
|
|
|
if ( length > 0 )
|
|
{
|
|
char new_string[vMAX_TEXT_LENGTH];
|
|
|
|
// initialize string
|
|
strcpy( new_string, "" );
|
|
if ( mp_text )
|
|
strcpy( new_string, mp_text );
|
|
|
|
// add new text
|
|
strcat( new_string, pText );
|
|
|
|
SetText( new_string );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CTextElement::Backspace()
|
|
{
|
|
if ( !mp_text )
|
|
return false;
|
|
|
|
int length = strlen( mp_text );
|
|
|
|
if ( length == 0 )
|
|
return false;
|
|
|
|
// copy string
|
|
char new_string[vMAX_TEXT_LENGTH];
|
|
strcpy( new_string, mp_text );
|
|
|
|
// take care of escaped backslash char (ASCII code 92)
|
|
if ( length > 1 && new_string[length - 1] == 92 && new_string[length - 2] == 92 )
|
|
new_string[length - 2] = '\0';
|
|
|
|
// terminate
|
|
new_string[length - 1] = '\0';
|
|
SetText( new_string );
|
|
return true;
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::AttachBlurEffect()
|
|
{
|
|
#if BLUR_EFFECT_ON
|
|
if (!mp_blur_effect)
|
|
{
|
|
destroy_text_instances();
|
|
|
|
// create blur effect according to specs
|
|
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
|
|
mp_blur_effect = new CBlurEffect();
|
|
// mp_blur_effect->SetRGBA(m_rgba);
|
|
mp_blur_effect->SetRGBA( m_local_props.GetRGBA() );
|
|
Mem::Manager::sHandle().PopContext();
|
|
|
|
// get number of CText objects needed
|
|
// create 'em
|
|
create_text_instances(mp_blur_effect->GetNumEntries() + 1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::DetachBlurEffect()
|
|
{
|
|
if (mp_blur_effect)
|
|
{
|
|
destroy_text_instances();
|
|
delete mp_blur_effect;
|
|
mp_blur_effect = NULL;
|
|
create_text_instances(1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::SetShadowState(bool shadowOn)
|
|
{
|
|
if (shadowOn && !m_use_shadow)
|
|
{
|
|
m_use_shadow = true;
|
|
create_text_instances(m_num_tab_entries, true);
|
|
}
|
|
else if (!shadowOn && m_use_shadow)
|
|
{
|
|
m_use_shadow = false;
|
|
destroy_text_instances(true);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::SetShadowOff(float offX, float offY)
|
|
{
|
|
m_shadow_off_x = offX;
|
|
m_shadow_off_y = offY;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::update()
|
|
{
|
|
//Dbg_Message("I am drawing text element at (%.1f,%.1f), scale %.1f\n", m_summed_props.ulx, m_summed_props.uly, m_summed_props.scalex);
|
|
#if 0
|
|
if( mp_font )
|
|
{
|
|
mp_font->BeginText( *((uint32 *) &m_local_props.GetRGBA() ), m_summed_props.scalex);
|
|
mp_font->DrawString(mp_text, m_summed_props.ulx, m_summed_props.uly);
|
|
mp_font->EndText();
|
|
}
|
|
#else
|
|
|
|
if (!mp_text) return;
|
|
|
|
bool offscreen = false;
|
|
// HACK
|
|
if (m_summed_props.GetScreenUpperLeftY() < -200.0f || m_summed_props.GetScreenUpperLeftY() > 648.0f)
|
|
offscreen = true;
|
|
|
|
if (m_num_tab_entries)
|
|
{
|
|
Image::RGBA true_rgba = m_local_props.GetRGBA();
|
|
if (m_summed_props.alpha >= .0001f)
|
|
true_rgba.a = (uint8) ((float) m_local_props.GetRGBA().a * m_summed_props.alpha);
|
|
else
|
|
true_rgba.a = 0;
|
|
|
|
/*
|
|
-pass current pos, scale, alpha (local) to blur effect
|
|
-for each entry in table
|
|
-get pos, scale, alpa (in local, relative to current)
|
|
*/
|
|
|
|
mpp_text_req_tab[0]->SetFont(mp_font);
|
|
mpp_text_req_tab[0]->SetPos(m_summed_props.GetScreenUpperLeftX(), m_summed_props.GetScreenUpperLeftY());
|
|
mpp_text_req_tab[0]->SetRGBA(true_rgba, m_override_encoded_rgba);
|
|
mpp_text_req_tab[0]->SetScale(m_summed_props.GetScaleX(), m_summed_props.GetScaleY());
|
|
mpp_text_req_tab[0]->SetString(mp_text);
|
|
if (m_object_flags & CScreenElement::v3D_POS)
|
|
{
|
|
mpp_text_req_tab[0]->SetZValue(m_summed_props.GetScreenPosZ());
|
|
mpp_text_req_tab[0]->SetHidden( ( ( m_object_flags & CScreenElement::v3D_CULLED ) || IsHidden() ) );
|
|
} else {
|
|
mpp_text_req_tab[0]->SetPriority(m_z_priority);
|
|
mpp_text_req_tab[0]->SetHidden( ( offscreen || IsHidden() ) );
|
|
}
|
|
|
|
#if 0 // Garrett: Can't find window
|
|
// Update the clip window. This should only be done at init time, instead.
|
|
CWindowElement *p_window = get_window();
|
|
mpp_text_req_tab[0]->SetWindow(p_window->GetClipWindow());
|
|
#endif
|
|
|
|
|
|
if (m_use_shadow)
|
|
{
|
|
Image::RGBA true_rgba = m_shadow_rgba;
|
|
if (m_summed_props.alpha >= .0001f)
|
|
true_rgba.a = (uint8) ((float) m_shadow_rgba.a * m_summed_props.alpha);
|
|
else
|
|
true_rgba.a = 0;
|
|
mpp_shadow_req_tab[0]->SetFont(mp_font);
|
|
mpp_shadow_req_tab[0]->SetPos(m_summed_props.GetScreenUpperLeftX() + m_shadow_off_x, m_summed_props.GetScreenUpperLeftY() + m_shadow_off_y);
|
|
mpp_shadow_req_tab[0]->SetRGBA(true_rgba, m_override_encoded_rgba);
|
|
mpp_shadow_req_tab[0]->SetScale(m_summed_props.GetScaleX(), m_summed_props.GetScaleY());
|
|
mpp_shadow_req_tab[0]->SetString(mp_text);
|
|
if (m_object_flags & CScreenElement::v3D_POS)
|
|
{
|
|
mpp_shadow_req_tab[0]->SetZValue(m_summed_props.GetScreenPosZ());
|
|
mpp_shadow_req_tab[0]->SetHidden( ( ( m_object_flags & CScreenElement::v3D_CULLED ) || IsHidden() ) );
|
|
} else {
|
|
mpp_shadow_req_tab[0]->SetPriority(m_z_priority - .005f);
|
|
mpp_shadow_req_tab[0]->SetHidden( ( offscreen || IsHidden() ) );
|
|
}
|
|
}
|
|
}
|
|
if(mp_blur_effect)
|
|
{
|
|
/*
|
|
-pass current center pos, scale, alpha (local) to blur effect
|
|
-for each entry in table
|
|
-get center pos, scale, alpa (in local, relative to current)
|
|
*/
|
|
|
|
Dbg_MsgAssert(!(m_object_flags & CScreenElement::v3D_POS), ("Can't use motion blur on 3D positioned text"));
|
|
|
|
mp_blur_effect->Update();
|
|
|
|
// work out absolute center of this element
|
|
float center_x = m_summed_props.GetScreenUpperLeftX() + m_summed_props.GetScaleX() * m_base_w / 2.0f;
|
|
float center_y = m_summed_props.GetScreenUpperLeftY() + m_summed_props.GetScaleY() * m_base_h / 2.0f;
|
|
|
|
for (int i = 1; i < m_num_tab_entries; i++)
|
|
{
|
|
ConcatProps &blur_entry = mp_blur_effect->GetInfo(i-1);
|
|
|
|
Image::RGBA true_rgba = mp_blur_effect->GetRGBA();
|
|
true_rgba.a = (uint8) ((float) true_rgba.a * blur_entry.alpha);
|
|
|
|
float draw_pos_x = center_x - (blur_entry.GetScreenPosX() + m_base_w * blur_entry.GetScaleX() / 2.0f) * m_summed_props.GetScaleX();
|
|
float draw_pos_y = center_y - (blur_entry.GetScreenPosY() + m_base_h * blur_entry.GetScaleY() / 2.0f) * m_summed_props.GetScaleY();
|
|
float draw_scale_x = m_summed_props.GetScaleX() * blur_entry.GetScaleX();
|
|
float draw_scale_y = m_summed_props.GetScaleY() * blur_entry.GetScaleY();
|
|
|
|
mpp_text_req_tab[i]->SetPos(draw_pos_x, draw_pos_y);
|
|
mpp_text_req_tab[i]->SetRGBA(true_rgba, m_override_encoded_rgba);
|
|
mpp_text_req_tab[i]->SetScale(draw_scale_x, draw_scale_y);
|
|
mpp_text_req_tab[i]->SetPriority(m_z_priority - i * .01f);
|
|
|
|
mpp_text_req_tab[i]->SetFont(mp_font);
|
|
mpp_text_req_tab[i]->SetString(mp_text);
|
|
mpp_text_req_tab[i]->SetHidden( ( offscreen || IsHidden() ) );
|
|
|
|
if (m_use_shadow)
|
|
{
|
|
Image::RGBA true_rgba = m_shadow_rgba;
|
|
if (m_summed_props.alpha >= .0001f)
|
|
true_rgba.a = (uint8) ((float) m_shadow_rgba.a * m_summed_props.alpha);
|
|
else
|
|
true_rgba.a = 0;
|
|
mpp_shadow_req_tab[i]->SetPos(draw_pos_x + m_shadow_off_x * draw_scale_x,
|
|
draw_pos_y + m_shadow_off_y * draw_scale_y);
|
|
mpp_shadow_req_tab[i]->SetRGBA(true_rgba, m_override_encoded_rgba);
|
|
mpp_shadow_req_tab[i]->SetScale(draw_scale_x, draw_scale_y);
|
|
mpp_shadow_req_tab[i]->SetPriority(m_z_priority - i * .01f - .005f);
|
|
|
|
mpp_shadow_req_tab[i]->SetFont(mp_font);
|
|
mpp_shadow_req_tab[i]->SetString(mp_text);
|
|
mpp_shadow_req_tab[i]->SetHidden( ( offscreen || IsHidden() ) );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::create_text_instances(int numEntries, bool shadow_only)
|
|
{
|
|
Dbg_Assert(!mpp_shadow_req_tab);
|
|
|
|
if (m_use_shadow)
|
|
{
|
|
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
|
|
mpp_shadow_req_tab = new Nx::CText*[numEntries];
|
|
Mem::Manager::sHandle().PopContext();
|
|
for (int i = 0; i < numEntries; i++)
|
|
{
|
|
mpp_shadow_req_tab[i] = Nx::CTextMan::sGetTextInstance();
|
|
}
|
|
}
|
|
|
|
if (!shadow_only)
|
|
{
|
|
Dbg_Assert(!m_num_tab_entries);
|
|
|
|
m_num_tab_entries = numEntries;
|
|
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
|
|
mpp_text_req_tab = new Nx::CText*[numEntries];
|
|
Mem::Manager::sHandle().PopContext();
|
|
for (int i = 0; i < numEntries; i++)
|
|
{
|
|
mpp_text_req_tab[i] = Nx::CTextMan::sGetTextInstance();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextElement::destroy_text_instances(bool shadow_only)
|
|
{
|
|
if (mpp_shadow_req_tab)
|
|
{
|
|
for (int i = 0; i < m_num_tab_entries; i++)
|
|
{
|
|
Nx::CTextMan::sFreeTextInstance(mpp_shadow_req_tab[i]);
|
|
}
|
|
delete mpp_shadow_req_tab;
|
|
mpp_shadow_req_tab = NULL;
|
|
}
|
|
|
|
if (!shadow_only)
|
|
{
|
|
Dbg_Assert(m_num_tab_entries);
|
|
|
|
for (int i = 0; i < m_num_tab_entries; i++)
|
|
{
|
|
Nx::CTextMan::sFreeTextInstance(mpp_text_req_tab[i]);
|
|
}
|
|
delete mpp_text_req_tab;
|
|
m_num_tab_entries = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CTextBlockElement::CTextBlockElement() :
|
|
m_shadow_rgba(0, 0, 0, 128)
|
|
{
|
|
//Ryan("I am a new CTextBlockElement\n");
|
|
m_font = 0;
|
|
m_internal_scale = 1.0f;
|
|
m_line_spacing_scale = 1.0f;
|
|
m_total_height = 0.0f;
|
|
m_total_out_lines = 0;
|
|
|
|
mp_blur_effect = NULL;
|
|
m_allow_expansion = false;
|
|
|
|
m_override_encoded_rgba = false;
|
|
m_previous_override_rgba_state = false;
|
|
|
|
SetType(CScreenElement::TYPE_TEXT_BLOCK_ELEMENT);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
CTextBlockElement::~CTextBlockElement()
|
|
{
|
|
//Ryan("Destroying CTextBlockElement\n");
|
|
if (mp_blur_effect)
|
|
delete mp_blur_effect;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextBlockElement::SetProperties(Script::CStruct *pProps)
|
|
{
|
|
CScreenElement::SetProperties(pProps);
|
|
|
|
// must find this before setting font
|
|
if (pProps->GetFloat(CRCD(0x1fe341d2,"internal_scale"), &m_internal_scale))
|
|
m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
|
|
|
|
pProps->GetFloat(CRCD(0xe3fa22fc,"line_spacing"), &m_line_spacing_scale);
|
|
|
|
uint32 font_crc;
|
|
if (pProps->GetChecksum(CRCD(0x2f6bf72d,"font"), &font_crc))
|
|
SetFont(font_crc);
|
|
Dbg_MsgAssert(m_font, ("no font loaded"));
|
|
|
|
|
|
if (resolve_just(pProps, CRCD(0x67e093e4,"internal_just"), &m_internal_just_x, &m_internal_just_y))
|
|
m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
|
|
|
|
|
|
const char *pp_line_tab[32];
|
|
Script::CArray *p_text_array;
|
|
|
|
if (pProps->ContainsFlag( CRCD(0x322839a2,"allow_expansion") ))
|
|
m_allow_expansion = true;
|
|
|
|
if (pProps->GetText(CRCD(0xc4745838,"text"), pp_line_tab))
|
|
{
|
|
SetText(pp_line_tab, 1);
|
|
}
|
|
else if (pProps->GetArray(CRCD(0xc4745838,"text"), &p_text_array))
|
|
{
|
|
int num_entries = p_text_array->GetSize();
|
|
for (int i = 0; i < num_entries; i++)
|
|
pp_line_tab[i] = p_text_array->GetString(i);
|
|
|
|
SetText(pp_line_tab, num_entries);
|
|
}
|
|
|
|
/*
|
|
Property changes to be forwared to children follow:
|
|
*/
|
|
|
|
if (pProps->ContainsFlag(CRCD(0xd1653c6c,"blur_effect")) && !mp_blur_effect)
|
|
{
|
|
#if BLUR_EFFECT_ON
|
|
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
|
|
mp_blur_effect = new CBlurEffect();
|
|
// mp_blur_effect->SetRGBA(m_rgba);
|
|
mp_blur_effect->SetRGBA( m_local_props.GetRGBA() );
|
|
Mem::Manager::sHandle().PopContext();
|
|
#endif
|
|
}
|
|
else if (pProps->ContainsFlag(CRCD(0x4b73e4f8,"no_blur_effect")) && mp_blur_effect)
|
|
{
|
|
delete mp_blur_effect;
|
|
mp_blur_effect = NULL;
|
|
}
|
|
|
|
Image::RGBA blur_rgba;
|
|
if (resolve_rgba(pProps, CRCD(0x4f0e1182,"blur_rgba"), &blur_rgba) && mp_blur_effect)
|
|
{
|
|
mp_blur_effect->SetRGBA(blur_rgba);
|
|
}
|
|
|
|
Script::CPair shadow_offs;
|
|
if (pProps->GetPair(CRCD(0x2a1fe0cc,"shadow_offs"), &shadow_offs))
|
|
{
|
|
m_shadow_off_x = shadow_offs.mX;
|
|
m_shadow_off_y = shadow_offs.mY;
|
|
}
|
|
|
|
if (pProps->ContainsFlag(CRCD(0x8a897dd2,"shadow")))
|
|
m_use_shadow = true;
|
|
if (pProps->ContainsFlag(CRCD(0x95b5b9a0,"no_shadow")))
|
|
m_use_shadow = false;
|
|
|
|
resolve_rgba(pProps, CRCD(0x1e7bb1f5,"shadow_rgba"), &m_shadow_rgba);
|
|
|
|
if ( pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ) )
|
|
{
|
|
Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
|
|
m_previous_override_rgba_state = m_override_encoded_rgba;
|
|
}
|
|
if ( pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ) )
|
|
{
|
|
Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
|
|
m_override_encoded_rgba = m_previous_override_rgba_state;
|
|
}
|
|
|
|
if (pProps->ContainsFlag(CRCD(0xb7686f92,"override_encoded_rgba")))
|
|
m_override_encoded_rgba = true;
|
|
if (pProps->ContainsFlag(CRCD(0xd25bc6cd,"dont_override_encoded_rgba")))
|
|
m_override_encoded_rgba = false;
|
|
|
|
forward_properties_to_children();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextBlockElement::SetMorph(Script::CStruct *pProps)
|
|
{
|
|
if (mp_blur_effect)
|
|
{
|
|
// Distribute blur effect settings to children, account for different
|
|
// vertical positions of children relative to center of this
|
|
|
|
// cheap hack, update self so that contained elements have proper position
|
|
update();
|
|
|
|
Tmr::Time target_time;
|
|
const CBlurEffect::Props &blur_target = mp_blur_effect->SetMorph(pProps, &target_time);
|
|
//printf("%f\n", blur_target.topAlpha);
|
|
// hook up blur effect to children
|
|
|
|
CScreenElementPtr p_child = CScreenElement::GetFirstChild();
|
|
while(p_child)
|
|
{
|
|
|
|
Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
|
|
Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));
|
|
|
|
|
|
// "child blur target"
|
|
CBlurEffect::Props cbt = blur_target;
|
|
|
|
// find center of child relative to center of this
|
|
// (we know we'll be getting top of child, since that's the just this class sets)
|
|
float child_center_x, child_center_y;
|
|
p_child->GetLocalPos(&child_center_x, &child_center_y);
|
|
child_center_y += p_child->GetBaseH() / 2.0f;
|
|
float disp_y = m_base_h / 2.0f - child_center_y;
|
|
cbt.maxDisplacementY += blur_target.bottomScaleY * disp_y - disp_y;
|
|
|
|
CBlurEffect *p_child_blur = ((CTextElement *) ((CScreenElement *) p_child))->GetBlurEffect();
|
|
Dbg_Assert(p_child_blur);
|
|
p_child_blur->SetAllTargetProps(cbt, target_time);
|
|
p_child = p_child->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
CScreenElement::SetMorph(pProps);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextBlockElement::SetFont(uint32 font_checksum)
|
|
{
|
|
Dbg_MsgAssert(!m_font, ("font already set"));
|
|
m_font = font_checksum;
|
|
Dbg_MsgAssert(Nx::CFontManager::sTestFontLoaded(m_font), ("font %s isn't loaded", Script::FindChecksumName(m_font)));
|
|
|
|
// how many lines will fit?
|
|
|
|
Nx::CFont* p_font = Nx::CFontManager::sGetFont(m_font);
|
|
Dbg_MsgAssert(p_font, ("no font found"));
|
|
|
|
// see if word wraps past end of element
|
|
float word_w, word_h;
|
|
p_font->QueryString("AAA", word_w, word_h); // width and height in pixels
|
|
|
|
m_num_visible_lines = (int) m_base_h / (int) (word_h * m_internal_scale);
|
|
m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextBlockElement::SetText(const char **ppTextLines, int numLines)
|
|
{
|
|
Dbg_MsgAssert(m_object_flags & vFORCED_DIMS, ("no dimensions have been set"));
|
|
Dbg_MsgAssert(numLines < 256, ("large number of lines, probably not good"));
|
|
|
|
// char parsed_lines[MAX_LINES][MAX_CHARS];
|
|
|
|
CScreenElementManager* p_manager = CScreenElementManager::Instance();
|
|
|
|
// destroy any children that are present
|
|
SetChildLockState( UNLOCK );
|
|
CScreenElementPtr p_child = GetFirstChild();
|
|
while ( p_child )
|
|
{
|
|
CScreenElementPtr p_next = p_child->GetNextSibling();
|
|
p_manager->DestroyElement(p_child->GetID());
|
|
p_child = p_next;
|
|
}
|
|
Dbg_Assert( !GetFirstChild() );
|
|
|
|
Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
|
|
mpp_parsed_lines = new char*[MAX_LINES];
|
|
// (Mick) in order to avoid doing multiple allocations, we just
|
|
// allocate a single array big enough for all the lines
|
|
mpp_parsed_lines[0] = new char[MAX_CHARS * MAX_LINES];
|
|
Mem::Manager::sHandle().PopContext();
|
|
// then calculate the pointers for all the other lines, as an offset to this
|
|
for ( int l = 1; l < MAX_LINES; l++ )
|
|
{
|
|
mpp_parsed_lines[l] = mpp_parsed_lines[0] + MAX_CHARS*l;
|
|
}
|
|
for( int i = 0; i < MAX_LINES; i++ )
|
|
{
|
|
mpp_parsed_lines[i][0] = '\0';
|
|
}
|
|
|
|
m_out_char = 0;
|
|
m_virtual_out_line = 0;
|
|
m_out_line = 0;
|
|
m_current_line_width = 0.0f;
|
|
|
|
// reset number of visible lines
|
|
if ( m_allow_expansion )
|
|
{
|
|
m_base_h = 0.0f;
|
|
m_num_visible_lines = 0;
|
|
}
|
|
|
|
for ( int in_line = 0; in_line < numLines; in_line++ )
|
|
read_in_text_line( ppTextLines[in_line] );
|
|
|
|
// printf("line %d: %s\n", m_out_line, mpp_parsed_lines[m_out_line]);
|
|
// want to handle the "no text" case
|
|
if (numLines)
|
|
{
|
|
m_virtual_out_line++;
|
|
m_out_line++;
|
|
}
|
|
|
|
// we only to want to use the N most recent lines,
|
|
// where N is the number of lines that will fit
|
|
int buf_use_line = 0;
|
|
m_total_out_lines = m_out_line;
|
|
if ( m_virtual_out_line >= m_num_visible_lines )
|
|
{
|
|
// expand the element if we're supposed to
|
|
if ( m_allow_expansion )
|
|
{
|
|
Nx::CFont* p_font = Nx::CFontManager::sGetFont(m_font);
|
|
Dbg_MsgAssert(p_font, ("no font found"));
|
|
|
|
// change the height of the element
|
|
float word_w, word_h;
|
|
p_font->QueryString("AAA", word_w, word_h ); // width and height in pixels
|
|
m_base_h = m_virtual_out_line * (word_h * m_internal_scale * m_line_spacing_scale );
|
|
|
|
m_num_visible_lines = (int) m_base_h / (int) (word_h * m_internal_scale * m_line_spacing_scale );
|
|
|
|
// re-parse the text now that m_num_visible_lines has changed
|
|
// Mick: no need for this, since they are fixed size, we just overwrite them
|
|
// for (int l = 0; l < MAX_LINES; l++)
|
|
// {
|
|
// delete mpp_parsed_lines[l];
|
|
// mpp_parsed_lines[l] = new char[MAX_CHARS];
|
|
// }
|
|
m_out_char = 0;
|
|
m_virtual_out_line = 0;
|
|
m_out_line = 0;
|
|
m_current_line_width = 0.0f;
|
|
for (int i = 0; i < numLines; i++)
|
|
read_in_text_line(ppTextLines[i]);
|
|
|
|
// printf("changing m_total_out_lines from %i to %i\n", m_total_out_lines, m_num_visible_lines);
|
|
m_total_out_lines = m_num_visible_lines;
|
|
}
|
|
else
|
|
{
|
|
Dbg_MsgAssert( m_num_visible_lines, ("Text block element too small for one line") );
|
|
buf_use_line = m_virtual_out_line % m_num_visible_lines;
|
|
m_total_out_lines = m_num_visible_lines;
|
|
}
|
|
}
|
|
|
|
|
|
// create new children
|
|
m_total_height = 0.0f;
|
|
for (int i = 0; i < m_total_out_lines; i++)
|
|
{
|
|
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
|
|
CTextElement *p_new_elem = new CTextElement();
|
|
Mem::Manager::sHandle().PopContext();
|
|
p_manager->RegisterObject(*p_new_elem);
|
|
p_manager->SetParent(this, p_new_elem);
|
|
p_new_elem->SetFont(m_font);
|
|
p_new_elem->SetText(&mpp_parsed_lines[buf_use_line][0]);
|
|
p_new_elem->SetScale(m_internal_scale, m_internal_scale);
|
|
|
|
|
|
m_total_height += p_new_elem->GetBaseH() * m_internal_scale * m_line_spacing_scale;
|
|
buf_use_line++;
|
|
if (buf_use_line >= m_num_visible_lines)
|
|
buf_use_line = 0;
|
|
}
|
|
|
|
// delete the lines, now we've used them
|
|
// Mick: Now we just delete the array of chars, and the array of pointers
|
|
// for (int l = 0; l < MAX_LINES; l++)
|
|
// {
|
|
// delete mpp_parsed_lines[l];
|
|
// mpp_parsed_lines[l] = NULL;
|
|
// }
|
|
delete mpp_parsed_lines[0];
|
|
delete mpp_parsed_lines;
|
|
|
|
|
|
SetChildLockState(LOCK);
|
|
|
|
m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextBlockElement::SetText(const char *pTextLine)
|
|
{
|
|
SetText(&pTextLine, 1);
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CTextBlockElement::GetText( char* p_text, int size )
|
|
{
|
|
strcpy( p_text, "" );
|
|
int total_length = 0;
|
|
|
|
CScreenElementPtr p_child = GetFirstChild();
|
|
while( p_child )
|
|
{
|
|
Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
|
|
Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));
|
|
|
|
|
|
CScreenElementPtr p_next = p_child->GetNextSibling();
|
|
CTextElement* p_text_element = (CTextElement*)p_child.Convert();
|
|
|
|
char* p_text_element_string = p_text_element->GetText();
|
|
total_length += strlen( p_text_element_string );
|
|
|
|
if ( p_next )
|
|
total_length += 1;
|
|
|
|
if ( total_length < size )
|
|
{
|
|
strcat( p_text, p_text_element_string );
|
|
|
|
// add a space between lines if this isn't the last line
|
|
if ( p_next )
|
|
strcat( p_text, " " );
|
|
}
|
|
else
|
|
{
|
|
Dbg_MsgAssert( 0, ( "TextBlockElement::GetText - text too long" ) );
|
|
return false;
|
|
}
|
|
p_child = p_next;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
int CTextBlockElement::GetLength()
|
|
{
|
|
// printf( "CTextBlockElement::GetLength called on %s\n", Script::FindChecksumName( m_id ) );
|
|
int total_length = 0;
|
|
|
|
CScreenElementPtr p_child = GetFirstChild();
|
|
while( p_child )
|
|
{
|
|
Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
|
|
Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));
|
|
|
|
CScreenElementPtr p_next = p_child->GetNextSibling();
|
|
CTextElement* p_text_element = (CTextElement*)p_child.Convert();
|
|
total_length += p_text_element->GetLength();
|
|
|
|
if ( p_next )
|
|
total_length += 1;
|
|
|
|
p_child = p_next;
|
|
}
|
|
return total_length;
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CTextBlockElement::Backspace()
|
|
{
|
|
int length = GetLength();
|
|
|
|
if ( length == 0 )
|
|
return false;
|
|
|
|
Dbg_MsgAssert( length < MAX_EDITABLE_TEXT_BLOCK_LENGTH, ( "Backspace failed - string too long" ) );
|
|
char new_string[MAX_EDITABLE_TEXT_BLOCK_LENGTH];
|
|
GetText( new_string, MAX_EDITABLE_TEXT_BLOCK_LENGTH );
|
|
|
|
if ( !new_string )
|
|
return false;
|
|
|
|
// take care of escaped backslash char (ASCII code 92)
|
|
if ( length > 1 && new_string[length - 1] == 92 && new_string[length - 2] == 92 )
|
|
new_string[length - 2] = '\0';
|
|
|
|
// terminate
|
|
new_string[length - 1] = '\0';
|
|
SetText( new_string );
|
|
return true;
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
/*
|
|
Called from read_in_text_line()
|
|
*/
|
|
CTextBlockElement::EParseResult CTextBlockElement::parse_next_word(char *pWordBuf, const char **ppSource)
|
|
{
|
|
bool keep_scanning = true;
|
|
EParseResult result = NORMAL;
|
|
|
|
char *p_out = pWordBuf;
|
|
|
|
while (keep_scanning)
|
|
{
|
|
switch(**ppSource)
|
|
{
|
|
case ' ':
|
|
*p_out++ = **ppSource;
|
|
(*ppSource)++;
|
|
keep_scanning = false;
|
|
break;
|
|
case '\\':
|
|
{
|
|
(*ppSource)++;
|
|
if (**ppSource == '_')
|
|
{
|
|
*p_out++ = ' ';
|
|
(*ppSource)++;
|
|
}
|
|
else if (**ppSource == 'n')
|
|
{
|
|
(*ppSource)++;
|
|
result = NEXT_LINE;
|
|
keep_scanning = false;
|
|
}
|
|
else
|
|
{
|
|
// is \?, where ? is some character
|
|
// output both
|
|
*p_out++ = '\\';
|
|
*p_out++ = **ppSource;
|
|
(*ppSource)++;
|
|
}
|
|
break;
|
|
} // end case
|
|
case '\0':
|
|
result = END_OF_BUFFER;
|
|
keep_scanning = false;
|
|
break;
|
|
default:
|
|
{
|
|
*p_out++ = **ppSource;
|
|
(*ppSource)++;
|
|
break;
|
|
}
|
|
} // end switch
|
|
|
|
Dbg_Assert((int) p_out - (int) pWordBuf < MAX_CHARS - 1);
|
|
}
|
|
|
|
*p_out++ = '\0';
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
Called from SetText()
|
|
*/
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
|
|
void CTextBlockElement::read_in_text_line(const char *pText)
|
|
{
|
|
Nx::CFont* p_font = Nx::CFontManager::sGetFont(m_font);
|
|
Dbg_MsgAssert(p_font, ("no font found"));
|
|
|
|
const char *p_in = pText;
|
|
|
|
char p_word[MAX_CHARS];
|
|
|
|
EParseResult last_result = NORMAL;
|
|
|
|
// printf("reading in: %s\n", pText);
|
|
|
|
while ( last_result != END_OF_BUFFER )
|
|
{
|
|
EParseResult result = parse_next_word( p_word, &p_in );
|
|
|
|
// see if word wraps past end of element
|
|
float word_w, word_h;
|
|
p_font->QueryString(p_word, word_w, word_h); // width and height in pixels
|
|
word_w *= m_internal_scale;
|
|
|
|
// printf("result = %d, word = '%s', screen_w = %.2f\n", result, p_word, word_w);
|
|
|
|
// if adding this word makes the line too long
|
|
// or we had a newline char or similar
|
|
// or this word will overflow the buffer (unlikely, but possible)
|
|
if ( ( m_current_line_width > 0.0f && m_current_line_width + word_w > m_base_w )
|
|
|| ( last_result == NEXT_LINE )
|
|
|| ( m_out_char + strlen( p_word ) >= MAX_CHARS )
|
|
)
|
|
{
|
|
// printf("m_current_line_width = %f, word_w = %f, m_base_w = %f\n", m_current_line_width, word_w, m_base_w);
|
|
// printf("line %d: %s\n", m_out_line, mpp_parsed_lines[m_out_line]);
|
|
|
|
// if last character on line is a space, do away with it
|
|
if ( mpp_parsed_lines[m_out_line][m_out_char-1] == ' ' )
|
|
mpp_parsed_lines[m_out_line][m_out_char-1] = '\0';
|
|
|
|
// Wrap to next line.
|
|
// Once we fill all visible lines, we start recycling the buffer,
|
|
// keeping only the most recent lines, hence the virtual line stuff.
|
|
m_virtual_out_line++;
|
|
m_out_line++;
|
|
if ( m_out_line >= m_num_visible_lines )
|
|
m_out_line = 0;
|
|
m_out_char = 0;
|
|
m_current_line_width = 0.0f;
|
|
}
|
|
|
|
Dbg_Assert(m_out_line < MAX_LINES); // need to increase MAX_LINES if triggered
|
|
Dbg_MsgAssert(m_out_char + strlen(p_word) < MAX_CHARS, ("overflow, max allowed = %d, needed = %d\n%s%s\n", MAX_CHARS, m_out_char + strlen(p_word),&mpp_parsed_lines[m_out_line][0],p_word));
|
|
char *p_out = &mpp_parsed_lines[m_out_line][0] + m_out_char;
|
|
strcpy(p_out, p_word);
|
|
m_out_char += strlen(p_word);
|
|
m_current_line_width += word_w;
|
|
|
|
last_result = result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextBlockElement::update()
|
|
{
|
|
if (m_object_flags & CScreenElement::vCHANGED_STATIC_PROPS)
|
|
{
|
|
Dbg_Assert(m_total_height > .00001f || !m_total_out_lines);
|
|
|
|
// position children
|
|
float y = (m_internal_just_y + 1.0f) * (m_base_h - m_total_height) / 2.0f;
|
|
CScreenElementPtr p_child = GetFirstChild();
|
|
for (int i = 0; i < m_total_out_lines; i++)
|
|
{
|
|
Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
|
|
Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));
|
|
|
|
p_child->SetJust(m_internal_just_x, -1.0f);
|
|
// p_child->SetRGBA(m_rgba);
|
|
p_child->SetRGBA( m_local_props.GetRGBA() );
|
|
p_child->SetPos((m_internal_just_x + 1.0f) * m_base_w / 2.0f, y);
|
|
|
|
y += p_child->GetBaseH() * m_internal_scale * m_line_spacing_scale;
|
|
p_child = p_child->GetNextSibling();
|
|
}
|
|
forward_properties_to_children();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CTextBlockElement::forward_properties_to_children()
|
|
{
|
|
CScreenElementPtr p_child = CScreenElement::GetFirstChild();
|
|
while(p_child)
|
|
{
|
|
|
|
Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
|
|
Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));
|
|
|
|
if (mp_blur_effect)
|
|
((CTextElement *) ((CScreenElement *) p_child))->AttachBlurEffect();
|
|
else
|
|
((CTextElement *) ((CScreenElement *) p_child))->DetachBlurEffect();
|
|
if (m_use_shadow)
|
|
((CTextElement *) ((CScreenElement *) p_child))->SetShadowState(true);
|
|
else
|
|
((CTextElement *) ((CScreenElement *) p_child))->SetShadowState(false);
|
|
((CTextElement *) ((CScreenElement *) p_child))->SetShadowOff(m_shadow_off_x, m_shadow_off_y);
|
|
((CTextElement *) ((CScreenElement *) p_child))->SetShadowRGBA(m_shadow_rgba);
|
|
((CTextElement *) ((CScreenElement *) p_child))->SetEncodedRGBAOverride(m_override_encoded_rgba);
|
|
p_child = p_child->GetNextSibling();
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
// if enforce_max_width is true, the function will fail if a single word is
|
|
// bigger than the width of a line. That is, if the text would go outside
|
|
// the specified width and there's no way to wrap, concatenation will fail.
|
|
// Only the end of the string is checked. The text already in the element
|
|
// is assumed to be safe. Thus it's possible to have a textblock element go
|
|
// outside it's box by using SetText directly, rather than using concatenate
|
|
bool CTextBlockElement::Concatenate( const char* pText, bool enforce_max_width, bool last_line )
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
int length = GetLength();
|
|
Dbg_MsgAssert( length < (int)( MAX_EDITABLE_TEXT_BLOCK_LENGTH - strlen( pText ) ), ( "TextBlock too long to concatenate" ) );
|
|
#endif
|
|
|
|
// setup new, unwrapped string
|
|
char* p_new_string = new char[MAX_EDITABLE_TEXT_BLOCK_LENGTH];
|
|
GetText( p_new_string, Front::MAX_EDITABLE_TEXT_BLOCK_LENGTH );
|
|
strcat( p_new_string, pText );
|
|
|
|
// enforce max width but allow them to add a space.
|
|
if ( (enforce_max_width && strcmp( pText, " " ) != 0) || last_line)
|
|
{
|
|
Nx::CFont* p_font = Nx::CFontManager::sGetFont( m_font );
|
|
Dbg_MsgAssert( p_font, ( "no font found" ) );
|
|
|
|
// parse through the new text word by word
|
|
const char *p_in = p_new_string;
|
|
char p_word[MAX_CHARS];
|
|
|
|
// skip up to the last word
|
|
EParseResult last_result = NORMAL;
|
|
while ( last_result != END_OF_BUFFER )
|
|
{
|
|
last_result = parse_next_word( p_word, &p_in );
|
|
}
|
|
|
|
// check that each word is not longer than the max width
|
|
float word_w, word_h;
|
|
p_font->QueryString( p_word, word_w, word_h ); // width and height in pixels
|
|
word_w *= m_internal_scale;
|
|
if ( word_w > m_base_w )
|
|
{
|
|
// too long!
|
|
delete p_new_string;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
SetText( p_new_string );
|
|
delete p_new_string;
|
|
return true;
|
|
}
|
|
|
|
|
|
}
|